时间轴

2025-10-15

init


题目:

双指针

双指针分别指向s和t,如果匹配上了就两个指针都向前移动,如果没有匹配上则指向t的指针向前移动,注意空字符串要单独处理。

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
#include <string>
using std::string;

class Solution {
public:
bool isSubsequence(string s, string t)
{
// 判断s是否为t的子字符串
int i = 0, j = 0;
int s_len = s.size();
int t_len = t.size();

if (s_len == 0) {
return true;
}
while (i < s_len && j < t_len) {
if (s[i] == t[j]) {
i++;
}
j++;
}
if (i == s_len && s[i - 1] == t[j - 1]) {
return true;
}
return false;
}
};

#include <stdio.h>
int main()
{
Solution solution;
string s = "abc";
string t = "ahbgdc";
printf("%d\n", solution.isSubsequence(s, t));
}

动态规划

考虑前面的双指针的做法,我们注意到我们有大量的时间用于在 t 中找到下一个匹配字符。这样我们可以预处理出对于 t 的每一个位置,从该位置开始往后每一个字符第一次出现的位置。我们可以使用动态规划的方法实现预处理:

令$ f[i][j]$ 表示字符串 t 中从位置 i 开始往后字符 j 第一次出现的位置。

状态转移时:如果 t 中位置 i 的字符就是 j,那么 $f[i][j]=i$,否则 j 出现在位置 i+1 开始往后,即 $f[i][j]=f[i+1][j]$,因此我们需要从后向前构建dp

状态转移方程是:

边界条件为:

其中:

  • $i$ 表示字符串 $t$ 中的位置(从 0 开始,0 ≤ i ≤ m)
  • $j$ 表示字符的编号(通常 ‘a’ 对应 0,’b’ 对应 1,…,’z’ 对应 25)
  • $f[i][j] = m$ 表示从位置 $i$ 开始往后不存在字符 $j$
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
class Solution {
public:
bool isSubsequence(string s, string t) {
int n = s.size(), m = t.size();
// m+1是为了处理边界情况
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;
}
};