判断子序列:
编辑距离入门问题
-
数组定义: 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]
-
递推公式:
if (s[i - 1] == t[j - 1]), 说明t中找到了一个字符在s中也出现了, 那么dp[i-1][j-1] + 1
if (s[i - 1] != t[j - 1]), 相当于t要删除元素,继续匹配, 删除元素就是dp[i][j-1]
-
初始化: 根据递推公式可以发现dp[0][0]和dp[i][0]是必须初始化的, 是为0的,可以省略
-
最后判断长度是否相等
细节统一:
- 遍历终止条件: 由于本题设定的是len-1的长度, 所以遍历完要=len
- 最后return 条件: 之前的dp数组定义是xxx位置, 现在是xxx长度, 所以有所不同
class Solution {
public boolean isSubsequence(String s, String t) {
int len1 = s.length();
int len2 = t.length();
int[][] dp = new int[len1 + 1][len2 + 1];
//直接开始遍历, 注意两个初始化定义是i-1;j-1, 所以<=
for(int i = 1; i <= len1; i++){
for(int j = 1; j <= len2; j++){
//这里有两种情况
//如果相等就+1
if(s.charAt(i - 1) == t.charAt(j - 1)){
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
//否则就得把t数组减一个
dp[i][j] = dp[i][j - 1];
}
}
}
//最后判断一下数组长度是不是一模一样
//由于定义, 这里不是len1-1
if(dp[len1][len2] == len1){
return true;
} else {
return false;
}
}
}
双指针解法:
class Solution {
public boolean isSubsequence(String s, String t) {
int len1 = s.length();
int len2 = t.length();
int i = 0, j = 0;
while(i < len1 && j < len2){
if(s.charAt(i) == t.charAt(j)){
//i最后用来判断是否成功移动到s的末尾了
i++;
}
//j一直向前移动
j++;
}
return i == len1;
}
}
不同的子序列
(这一题和上一题s和t反过来了, 现在t是子序列)
给两个字符串, 计算s的子序列中t出现的个数(有多少种方式可以获得子序列)
- 数组定义: 以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]
- 递推公式:
- 如果相等: 也有两种情况, 因为可以有不同的组合来获得t(所以j固定)
- 所以就是dp[i-1][j-1] + dp[i-1][j]
- 如果不相等: 那就还是dp[i-1][j], (现在的j是子序列哦)
- 如果相等: 也有两种情况, 因为可以有不同的组合来获得t(所以j固定)
- 初始化: dp[i][0]表示, 以i-1为结尾的s, 同时子序列是空的, 所以是1(删除所有元素之和, 剩下的匹配[0]的个数还是1), dp[0][j]就肯定是0了(同样可以忽略)
- 遍历顺序: 仍然是从左上角开始
class Solution {
public int numDistinct(String s, String t) {
int len1 = s.length();
int len2 = t.length();
int[][] dp = new int[len1 + 1][len2 + 1];
//初始化先, 这里是初始化[i][0]; 下面初始化[0][j]可以忽略
for(int i = 0; i <= len1; i++){
dp[i][0] = 1;
}
//开始遍历
for(int i = 1; i <= len1; i++){
for(int j = 1; j <= len2; j++){
//如果相等, 那么加上两种情况的组合个数
if(s.charAt(i - 1) == t.charAt(j - 1)){
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
//不相等, 那么s-1继续找
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[len1][len2];
}
}