给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,”ace”是”abcde”的一个子序列,而”aec”不是)。
进阶:
如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10 亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?
分析
这道题用动态规划也不太容易明白。我们首先还是先看 dp[i] 的定义。
dp[i][j]
:代表 i
位置之后,j
字符第一次出现的位置。这个不好理解,我们来看如下图片。
行代表 “aazbdz” 中的每一个字符,列代表每一个字符在某个字符之后出现的第一个位置。
- 值 6 代表,该行字符之后,没有该字符串。
比如 “z” 字符之后,再也没有字符了,所以有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| dp[5][0]=6 dp[5][1]=6 ...
dp[5][25]=5
|
完整代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| class Solution { public: bool isSubsequence(string s, string t) { int n = s.size(), m = t.size();
vector<vector<int> > f(m + 1, vector<int>(26, 0));
for (int i = 0; i < 26; i++) { f[m][i] = m; } for (int i = m - 1; i >= 0; i--) { for (int j = 0; j < 26; j++) { if (t[i] == j + 'a') f[i][j] = i; else f[i][j] = f[i + 1][j]; } }
int add = 0; for (int i = 0; i < n; i++) { if (f[add][s[i] - 'a'] == m) { return false; } add = f[add][s[i] - 'a'] + 1; } return true; } };
|
经验教训
这道题有三个关键点
- 建立 某个字符之后,a-z 第一次出现的位置 的 表 ,即构建
dp[i][j]
,⚠️:是从后往前构建
- 字符的匹配是跳跃式的。对于很长的 s,很多的 s,这比双指针的效率要好。