leetcode c++未初始化_LeetCode基础算法题第165篇:392题的扩展题的解法

技术提高是一个循序渐进的过程,所以我讲的leetcode算法题从最简单的level开始写的,然后到中级难度,最后到hard难度全部完。目前我选择C语言,Python和Java作为实现语言,因为这三种语言还是比较典型的。由于篇幅和精力有限,其他语言的实现有兴趣的朋友请自己尝试。

如果有任何问题可以在文章后评论或者私信给我。

如果有朋友希望我讲些其他话题,请在评论区留言或者私信给我。

持续分享,敬请关注。

LeetCode 392 的扩展题

问题描述:

给定两个字符串 s 和 t ,判断 s 是否为 t 的子序列。假设 s 和 t 仅包含英文小写字母。字符串 t 可能会是一个非常长的字符串(长度可以达到500,000),而 s 是个比较短的字符串(长度 <=100)。

字符串的子序列是指通过删除一些(也可不删除)原始字符串中的某些字符,但是不会调整剩余字符的相对位置,这样形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

如果有很多输入 S,记作S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。

C语言实现:

在《LeetCode 392》中,算法的复杂是O(len(t))。而t的长度可能会非常的大,达到50万,那么如果有10亿个s的输入,这个时间开销是很大的。

我们要想到一种解决办法,让整个算法的复杂度依赖s,而不是t,毕竟s的长度最大只有100,这样要快的多。

我的实现是先根据t建立一个大小为26*100的二维表 map,用来记录t中每个字母的位置分布信息。

其中26对应26个小写字母;

而100是因为,对于每个s,长度不会超过100, 所以如果s仅仅由一个字母组成,那么这个字母最多也仅仅会出现100次,所以如果t中某个字母出现超过100次,那么后面再出现的话,这个新的位置信息可以忽略掉。

当我们建立好map这个表以后,我们就可以遍历每一个s。用s的每个字符c依次查询map表,获取字符c在t中出现的位置。

正如《LeetCode 392》所说的,如果s是t的子序列,那么s中每个字符c的下标应该是绝对递增的。我们正是依据这一点来确定s是不是t的子序列。

这种查表的时间复杂度就是O(len(s))。

下面是这个算法的示意图,后面在详细描述代码的时候,请记得看看回头这张图

8a718d77671c797300a097f05a8a3c90.png

详细代码如下:

8d956c2a8a6515dcb3d7d299a497f8e4.png

我们将map定义为全局变量,这是因为,当map没建立(初始化)的时候,我们用下面定义的函数initMap完成这个工作后,后面调用isSubsequence检测s是否是t子序列的时候,就可以直接使用了,无需再做了。

变量hasInit是用来检测map是否已经建立完成,如果没有就调用initMap来做。

在initMap函数中,定义一个长度为26的整型数组idx,用来在遍历t的时候,记录某个字母出现了几次;我们通过t[i] - 'a'来定位字母t[i]的位置信息在map中存储位置。同样可以找到idx中该字母记录的个数,如果idx中的记录表明,这个字母已经出现100了,那么就无须再记录位置信息了,继续下一个字母,否则我们就追加一个位置信息到map中。

注意,这里我们记录的位置信息是从1开始的,即位置信息比下标大1,这是因为,下标的其实值为0,而map表中,所有元素的初值也是0,如果下标作为位置信息,有时候无法判断一个字母是否在t中存在,试想,如果s="k", t="abc"的例子。

isSubsequence函数中:

  • 第一句在map没有建立的时候,调用initMap来创建。
  • 这里idx和initMap中的idx作用是一样的,只是这里是为s服务的。
  • 定义pre作为s中前一个字母的位置信息(如上所诉,位置信息是从1开始的),初始是0;然后遍历s:我们检查当前字母的位置信息是否比上一个字母的位置信息(存在pre中)大,如果不是,就说明s不是t的子序列,直接返回false,否则将当前字母的位置信息赋值给pre,继续下一轮比较。

Java语言实现:

383ce99b5f006a0e5a886bf9f7c10ec5.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值