面试经典150题——判断子序列

​"Success is not final, failure is not fatal: It is the courage to continue that counts." 

- Winston Churchill

brown cardboard box with yellow light

1. 题目描述

2.  题目分析与解析

2.1 思路一——双指针

按照双指针的解法应该大家都能比较快的想出来,就是一个指针pointS指向字符串s,一个指针pointT指向字符串t,通过从前向后遍历t字符串,判断pointT指向的当前字符和pointS的字符是否相等,相等就将pointS指针也后移,如果pointS能够遍历结束则说明满足条件,返回true。

2.2 思路二——通过一个二维数组存放t字符串中每个字符及其对应下标

由于题目的进阶版本要求:

如果有大量输入的S,称作S1,S2,….,Sk其中k>=10亿,你需要依次检查它们是否为T的子序列。在这种情况下,你会怎样改变代码?

如果每个s都需要去遍历一遍t字符串,那么时间开销是很大的,所以我们现在就要想办法把 t 中的信息怎么存储起来来换取时间。之所以是需要存储 t 中的信息,是因为我们每一个 s 字符串是不相同的,而每一次的 t 字符串都是相同的,也就是说对于一个多次使用的东西,我们要想办法把他的信息最不能得高效的去利用。

因为我们想要达到的目的是遍历每一个s字符串,要在t中找到是否存在该序列,我们就可以通过遍历s的每一个字符,看t中是否有满足的匹配字符——不仅字符要匹配,而且下标要呈递增的模式

  • 因此我们就可以把 t 字符串中每一个字符的下标存储起来,这样我们就能够得到一个类似于二维数组的结构,如下:

  • 而后对于每一个s字符串,遍历,根据当前字符找到对应的键,在下标数组中找到一个合适的下标,能找到就继续,不能找到就返回false。

  • 这个合适的下标指的是:当前选择的下标要比上一次选择的下标大,保证字符间顺序。

  • 同时这个寻找合适下标的过程,是可以运用二分查找的,这是因为我的下标数组肯定都是有序的,因为我是从前到后遍历t字符串,这样形成的下标数组肯定是升序的,给使用二分查找创造了条件。

算法执行主要步骤:

  1. 通过遍历长字符串t,把每一个字符出现的位置记录下来

  2. 然后遍历短字符串s,查找对应的字符在键值和下标数组的hashMap中是否存在,如果存在找到满足下标大于前一个字符在长字符串中下标的字符

    • 该查找过程可以使用二分查找

    • 也可以通过一个新的数组记录上一次在该字符处使用的位置

主要讲一下通过一个新的数组记录上一次在该字符处使用的位置的方法,见下图:

上图展示了找目标字符串s的前三个字符的过程,当找最后一个字符a时,因为我当前a对应的数组的值为0,所以下一次我就只需要从1处找是否有下标满足大于前一个下标也就是c的下标5的目标,如下:

3. 代码实现

3.1 思路一——双指针

    // 判断子序列public boolean isSubsequence(String s, String t) {// 解题思路// 1. 双指针判断是否为子序列,一个指向s,一个指向t,//    如果s的指针指向的字符等于t的指针指向的字符,s的指针向后移动//    最后判断s的指针是否指向了s的末尾// 2. 时间复杂度O(n)// 3. 空间复杂度O(1)// 4. 代码实现int i = 0, j = 0;if (s.length == 0){return true;​​}while (i < s.length() && j < t.length()) {if (s.charAt(i) == t.charAt(j)) {i++;}j++;if (i == s.length()) {return true;}}return false;}

3.2 思路二——二维数组

3.2.1 使用二分查找
class Solution {public boolean isSubsequence(String s, String t) {int n = s.length(), m = t.length();// 通过遍历长字符串,把每一个字符的位置记录下来HashMap<Character, ArrayList<Integer>> hashMap = new HashMap<>();for (int i = 0; i < m; i++) {char ch = t.charAt(i);if (!hashMap.containsKey(ch)) {hashMap.put(ch, new ArrayList<>());}hashMap.get(ch).add(i);}// 用来记录上一个字符的索引int preIndex = -1;for (int i = 0; i < n; i++) {// 方法1:使用二分查找在hashMap.get(s.charAt(i))找到大于preIndex得最小值if (!hashMap.containsKey(s.charAt(i))) {return false;}int left = 0;int right = hashMap.get(s.charAt(i)).size() - 1;// 二分查找过程while (left < right) {int mid = left + (right - left) / 2;if (hashMap.get(s.charAt(i)).get(mid) <= preIndex) {left = mid + 1;} else {right = mid;}}if (hashMap.get(s.charAt(i)).get(left) <= preIndex) {return false;}preIndex = hashMap.get(s.charAt(i)).get(left);    }return true;}
}
3.2.2 使用数组记录下标
    public boolean isSubsequence3(String s, String t) {int n = s.length(), m = t.length();// 通过遍历长字符串,把每一个字符的位置记录下来HashMap<Character, ArrayList<Integer>> hashMap = new HashMap<>();HashMap<Character, Integer> indexMap = new HashMap<>();for (int i = 0; i < m; i++) {char ch = t.charAt(i);if (!hashMap.containsKey(ch)) {hashMap.put(ch, new ArrayList<>());}hashMap.get(ch).add(i);}// 给indexArray初始化for (Character c : hashMap.keySet()) {indexMap.put(c, -1);}// 用来记录上一个字符的索引int preIndex = -1;// 然后遍历短字符串,看其相应的顺序,对应在hashMap对应得Array中最小的索引,最后看是否能找全for (int i = 0; i < n; i++) {// 没有该字母 || (该字母的索引已经到末位&&该字母的索引不是-1也就是不是第一个字母)if ((!hashMap.containsKey(s.charAt(i)) || (hashMap.get(s.charAt(i)).get(hashMap.get(s.charAt(i)).size() - 1) <= indexMap.get(s.charAt(i)))  && indexMap.get(s.charAt(i)) != -1)) {return false;}// 理论上的下一个目标位置int index = indexMap.get(s.charAt(i)) + 1;// 有对应的下标if (index < hashMap.get(s.charAt(i)).size()) {// 找到大于preIndex得最小值while (preIndex >= hashMap.get(s.charAt(i)).get(index)) {index++;if (index == hashMap.get(s.charAt(i)).size()) {break;}}//没找到if (index == hashMap.get(s.charAt(i)).size()) {return false;}//找到了indexMap.put(s.charAt(i), index);preIndex = hashMap.get(s.charAt(i)).get(index);}//无对应的字母键else {return false;}}return true;}

4. 运行结果

4.1 双指针

4.2 二维数组——二分和数组记录下表相同

5. 相关复杂度分析

5.1 双指针

  • 时间复杂度:O(n+m),n为字符串t的长度,m为字符串s的长度。循环同时对s和t进行,每次无论是匹配成功还是失败,都有至少一个指针发生右移,两指针能够位移的总距离为n+m。

  • 空间复杂度:0(1),声明了2个变量

5.2 二维数组(二分查找)

  • 时间复杂度:O(MlogN+N),N为字符串s的长度,M为字符串t长度。对t中的每个字符都需要logN的查找一次;而且为了处理出待查找的序列,还需要O(N)的遍历字符串S。

  • 空间复杂度:O(N),需要一个大小为N的哈希表。

本人公众号,专注简单易懂的算法解析

下一篇:使用动态规划解决该题目

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/454146.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【漏洞复现】SpringBlade export-user接口存在SQL注入漏洞

漏洞描述 SpringBlade 是一个由商业级项目升级优化而来的微服务架构 采用Spring Boot 2.7 、Spring Cloud 2021 等核心技术构建,完全遵循阿里巴巴编码规范。提供基于React和Vue的两个前端框架用于快速搭建企业级的SaaS多租户微服务平台。SpringBlade export-user接口存在SQL注…

springboot+vue实现excel导出

后端 导入pom依赖 <dependency>x<groupId>cn.afterturn</groupId><artifactId>easypoi-spring-boot-starter</artifactId><version>4.2.0</version> </dependency> Entity实体类 这里以User为例&#xff0c;可按照自己实际…

LabVIEW智能温度直流模件自动测试系统

LabVIEW智能温度直流模件自动测试系统 自动化测试系统在提高测试效率和准确性方面发挥着越来越重要的作用。介绍了一种基于LabVIEW的智能温度直流模件&#xff08;TDCA&#xff09;自动测试系统的设计与实施&#xff0c;旨在提高测控装置的产品质量。 系统的硬件平台主要由PS…

AQS简介、AQS实现原理、线程夺取锁失败 AQS队列的变化、线程被唤醒时 AQS队列的变化

AQS AQS简介AQS实现原理场景01-线程抢夺锁失败时&#xff0c;AQS队列的变化场景02-线程被唤醒时&#xff0c;AQS队列的变化 AQS简介 AQS(全称AbstractQueuedSynchronizer)即队列同步器。它是构建锁或者其他同步组件的基础框 架(如ReentrantLock、ReentrantReadWriteLock、Sema…

Dell服务器iDRAC9忘记密码, 通过RACADM工具不重启 重置密码

系列文章目录 文章目录 系列文章目录前言一、RACADM工具二、linux环境1.解压安装RACADM工具测试RACADM工具重置iDRAC密码 Windows环境 前言 一、RACADM工具 RACADM工具 官网参考信息 https://www.dell.com/support/kbdoc/zh-cn/000126703/%E5%A6%82%E4%BD%95-%E9%87%8D%E7%BD…

web前端-------弹性盒子(2)

上一讲我们谈的是盒子的容器实行&#xff0c;今天我们来聊一聊弹性盒子的项目属性&#xff1b; *******************&#xff08;1&#xff09;顺序属性 order属性&#xff0c;用于定义容器中项目的出现顺序。 顺序属性值&#xff0c;为整数&#xff0c;可以为负数&#xff…

k8s-常用工作负载控制器(更高级管理Pod)

一、工作负载控制器是什么&#xff1f; 二、Deploymennt控制器&#xff1a;介绍与部署应用 部署 三、Deployment控制器&#xff1a;滚动升级、零停机 方式一&#xff1a; 通个加入健康检查可以&#xff0c;看到&#xff0c;nginx容器逐个被替代&#xff0c;最终每个都升级完成&…

算法学习——华为机考题库10(HJ64 - HJ69)

算法学习——华为机考题库10&#xff08;HJ64 - HJ69&#xff09; HJ64 MP3光标位置 描述 MP3 Player因为屏幕较小&#xff0c;显示歌曲列表的时候每屏只能显示几首歌曲&#xff0c;用户要通过上下键才能浏览所有的歌曲。为了简化处理&#xff0c;假设每屏只能显示4首歌曲&a…

幻兽帕鲁客户端存档文件 - 云上备份和恢复教程

本文将详细介绍如何将幻兽帕鲁游戏客户端的存档文件备份至云端&#xff0c;以及如何从云端恢复存档数据至本地。 一、游戏存档备份场景 幻兽帕鲁的游戏进度存储在电脑本地磁盘上&#xff0c;游戏中创建的每个世界都对应一个本地存档文件夹。在玩游戏过程中&#xff0c;客户端…

xlsx xlsx-style 使用和坑记录

1 安装之后报错 npm install xlsx --savenpm install xlsx-style --save Umi运行会报错 自己代码 import XLSX from "xlsx"; import XLSXStyle from "xlsx-style";const data [["demo1","demo2","demo3","demo4&quo…

linux中的gdb调试

gdb是在程序运行的结果与预期不符合时&#xff0c;可以使用gdb进行调试 注意&#xff1a;使用gdb调试时要在编译上加-g参数 gcc -g -c hello.c 启动gdb调试&#xff1a; gdb file 对gdb进行调试 设置运行参数&#xff1a; set args 可指定运行参数 show args 可以查…

群晖NAS开启FTP服务结合内网穿透实现公网远程访问本地服务

⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 文章目录 ⛳️ 推荐1. 群晖安装Cpolar2. 创建FTP公网地址3. 开启群晖FTP服务4. 群晖FTP远程连接5. 固定FTP公网地址6. 固定FTP…