题目:
双指针
双指针分别指向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) { 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(); 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; } };
|