![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/9654325d36caf4dbeb110f40417e03c0.png)
1. 双指针
解题思路:时间复杂度O(
s
+
t
s+t
s+t)两个字符串都遍历的一遍,空间复杂度O(
1
1
1) |
---|
- 一个指针指向s的字符,一个指针指向t的字符
- 如果两个字符对应,则匹配成功一个字符,两个指针一起后移
- 如果两个字符没有对应上,则t的指针后移,试图继续和s的当前字符匹配
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/280389246bb7d4feae02a6b45b224607.png)
class Solution {
public boolean isSubsequence(String s, String t) {
int sLen = s.length(),tLen = t.length();
char[] sCA = s.toCharArray(),tCA = t.toCharArray();
int sIndex = 0;
for(int i = 0;i<tLen;i++){
if(sIndex<sLen && sCA[sIndex] == tCA[i]) sIndex++;
}
return sIndex == sLen;
}
}
2. 动态规划
上面的方法时间复杂度很低,但是如果有1亿个s想要判断是否是t的子序列,就必须先对t进行预处理,也就是只有预处理的时候会慢,之后处理s的时候,会非常快。
- 前缀树,把t所有可能得前缀预处理,后续直接用s匹配即可
- 动态规划仿前缀树。使用二维数组,保存如果想在当前位置之后找到特定字母,是否可以找到,如果能找到,应该跳转到哪个位置找
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/02a21dffef331a9ea372f5e3fc7f1472.png)
解题思路:时间复杂度O(
t
∗
26
+
s
t*26 + s
t∗26+s),空间复杂度O(
t
∗
26
t*26
t∗26) |
---|
- 时间复杂度:26是因为t和s由26个字母组成。dp数组预处理的时间复杂度是O(
t
∗
26
t*26
t∗26)。之后查找t的子序列,时间复杂度最多不超过O(
s
s
s),也就是完全可以匹配时,才会消耗s时间复杂度,而如果s中只有一个字母可以完成匹配,则与dp数组比较2次即可判断无法完全匹配成功
- DP数组及下标含义
我们要求出的是
在t字符串的i以及i之后的位置,想要匹配指定字母,是否可以匹配到,如果能应该去哪个位置匹配,那么dp数组中存储的
就是当前i位置为起始,最近的一个指定字母出现的位置。要求出谁的
位置。显然是到达i位置后,指定字母的出现位置,那么下标就是代表现在到了t字符串哪个位置,我们要在当前位置找哪个字母
。显然,需要两个下标表示,一个下标代表当前t的位置,另一个下标表示我们接下来想要匹配的是26个字母中的哪一个。故这道题的dp数组需要二维数组
- 递推公式
- 假设t字符串的长度为m,我们建立dp大小为dp[m][26]. 因为下标从0开始,正好dp[m-1]是t的最后一个字符位置。而dp[m]行表示字符串结束位置。
- 我们先将dp[m]行全部赋值为m,表示在当前位置匹配任何字母都是匹配不到的
- 然后处理dp[m-1]也就是t的最后一个位置。假设t最后一个位置是a.那么dp[m-1][0]位置,就表示t的最后一个位置为a字母。将其设置为m-1. 而其余字母位置,dp[m-1][1-26]都为m(dp[m][1-26]),表示从m-1位置找除了a以外的字母,都是找不到的。
- 同理,dp[m-2]行也就是t的倒数第二个位置
- 假设这个位置字母也是a,那么对于dp[m-2][0] = m-2,表示m-2行找字母a,m-2行就是最近的位置,其余位置都为m-1行的状态 = dp[m-1][1-26]
- 假设这个位置是b。那么对于dp[m-2][1] = m-2,表示m-2行找字母b,m-2行就是最近的位置
- 而dp[m-2][0] = dp[m-1][0] = m-1,为什么?因为我们知道dp[m-1]位置字母是a,那么我们现在想在m-2后面找a的最近出现位置,那么自然是m-1位置。
- 因此递推公式为
- m行dp[m][1-26] = m,表示m是不可达区间,m表示当前位置和之后的位置找指定字母是找不到的
- 其余的dp[i][j],如果t[i] = j 则dp[i][j] = i.表示如果当前i位置出现字母是j,那么当前位置及之后,j字母最早出现位置就是i
- 而剩余的t[i] != j的位置,dp[i][j] = dp[i+1][j].表示如果当前i位置出现字母不是j,那么去问问i+1行吧
- dp数组初始化
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5dd7d53aae9ec0d24e02aeb213c1e6b1.png)
- 数组遍历顺序:肯定是先行后列,因为行是当前t的字符位置,列是26个字母中的哪一个。主角是对t进行预处理,所以先行后列
- 打印dp数组(自己生成dp数组后,将dp数组输出看看,是否和自己预想的一样。)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/78bf02b8a1636884cb08247ba2e48993.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/99b95a1bc02f9d0d613c23c3747593a5.png)
class Solution {
public boolean isSubsequence(String s, String t) {
int n = s.length(), m = t.length();
int[][] f = new int[m + 1][26];
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.charAt(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.charAt(i) - 'a'] == m) return false;
add = f[add][s.charAt(i) - 'a'] + 1;
}
return true;
}
}