题目
392. 判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,”ace”是”abcde”的一个子序列,而”aec”不是)。
示例 1:
1 | s = "abc", t = "ahbgdc" |
返回 true.
示例 2:
1 | s = "axc", t = "ahbgdc" |
返回 false.
后续挑战 :
如果有大量输入的 S,称作S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
答案
双指针
1 | class Solution: |
动态规划
官方题解里给出了一个动态规划的解法。
思路:
每个位置之后遇到26个小写字母的情况是如何的?
位置是0 ~ m-1
; 最后再加上一个全 m 的向量(26个小写字母)表示在 m 这个越界位置,遇到某个字母是不可能的。
那么对于 m-1
位置上的表示 m-1
开始往后某个字母的位置,因为m-1
开始只有一个字母,其他的都不可能出现了,所以这一列只有有的那个字母表示为 m-1
,其他的都是m
;以此往前完成 $f[i][j]$ 最终得到一个转换表。
后来不论来什么字符串在第一个位置查,从第一个位置开始出现那这个字符串第一个字符的位置。所以这个方法虽然复杂了一点,但是完全适用于问题中的后续挑战。
题解:
思路及算法
考虑前面的双指针的做法,我们注意到我们有大量的时间用于在 tt 中找到下一个匹配字符。
这样我们可以预处理出对于 tt 的每一个位置,从该位置开始往后每一个字符第一次出现的位置。
我们可以使用动态规划的方法实现预处理,令 $f[i][j]$ 表示字符串 tt 中从位置 $i$ 开始往后字符 $j$ 第一次出现的位置。在进行状态转移时,如果 $t$ 中位置 $i$ 的字符就是 $j$,那么 $f[i][j]=if[i][j]=i$,否则 $j$ 出现在位置 i+1i+1 开始往后,即 $f[i][j]=f[i+1][j]$,因此我们要倒过来进行动态规划,从后往前枚举 $i$。
这样我们可以写出状态转移方程:
1 | class Solution: |