这道题目算是 编辑距离问题 的入门题目(毕竟这里只是涉及到减法),慢慢的,后面就要来解决真正的 编辑距离问题了
https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html
我直接按逻辑去写
class Solution {
public boolean isSubsequence(String s, String t) {
int cur = 0;
int flag = 0;
loop:
for (int i = 0; i < s.length(); i++) {
for (int j = cur; j < t.length(); j++) {
if (s.charAt(i) == t.charAt(j)) {
//下一次从t串的 j + 1开始找,因为j是这次找到的。
cur = j + 1;
continue loop;
} else {
cur = j + 1;
}
}
if (cur >= t.length()) return false;
}
return true;
}
}
直接a了
但是一点也不dp,我直接看题解学习了。
判断s是不是t的子序列,其实就是判断两个字符串的公共子序列长度是不是s的长度。
那这道题本质就是最长公共子序列。
dp数组含义:dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。
没有
感觉我做这个最长公共子序列,有一点背答案了
class Solution {
public boolean isSubsequence(String s, String t) {
//dp
int[][] dp = new int[t.length() + 1][s.length() + 1];
int result = 0;
//init
//func
for (int i = 1; i < s.length() + 1; i++) {
for (int j = 1; j < t.length() + 1; j++) {
if (s.charAt(i - 1) == t.charAt(j - 1)) {
dp[j][i] = dp[j - 1][i - 1] + 1;
result = dp[j][i];
} else {
dp[j][i] = Math.max(dp[j - 1][i], dp[j][i - 1]);
}
}
}
return result == s.length() ? true : false;
}
}
但相对于刚讲过 392.判断子序列,本题 就有难度了 ,感受一下本题和 392.判断子序列 的区别。
https://programmercarl.com/0115.%E4%B8%8D%E5%90%8C%E7%9A%84%E5%AD%90%E5%BA%8F%E5%88%97.html
直接看题解!
这两道题给我弄的有点晕啊
这么说最长公共子序列那道题,max(dp[i-1][j], dp[i][j-1]) 就是模拟删除 s和删除 t两种?
到底是什么模拟删除?
这个部分还是和前两道题一样的
dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。
主要分两种情况考虑
当s[i - 1] 与 t[j - 1]相等时,dp[i][j] 有两部分组成,一个是用 s[i-1] 来匹配 t[j-1] , 那么这个个数就是dp[i-1][j-1]。
还有一部分是不用s[i-1] 来匹配。其实到这里我也挺懵逼,怎么都相同还不用s[i-1]来匹配。
例如: s:bagg 和 t:bag
我们遍历s[3] 时,是第二个g。如果用它来匹配t[2] 的g,这种情况s包含t的个数就是bag包含ba的个数. dp[i-1][j-1]
如果不用它来匹配t[2]的g,这种情况s包含t的个数就是bag包含bag的个数。就是模拟删除这个 s[3] 。dp[i-1][j]
所以dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
哎卧槽有点混乱了。
第二个是s[i - 1] 与 t[j - 1] 不相等的时候。dp[i][j]只有一部分组成,不用s[i - 1]来匹配(就是模拟在s中删除这个元素),即:dp[i][j] = dp[i - 1][j]
s 是行 遍历 i
t 是列 遍历 j
dp[0][j] 代表 s是空的,s里面有多少个t,那肯定0个。
dp[i][0] 代表 t是空的,s里面有多少个t,那有一个。
dp[0][]0] 代表s和t都是空的,s里面有多少个t,也是有一个。
比如 s = bagg , t = bag
最开始,两个元素就相同了。
那么这个时候的 dp[1][1] = 1 才对。
dp[1][1] = dp[0][0] + dp[0][1] = 1 + 0.
合理的
看得出直接正序了
最后返回的是dp[s.length][t.length]
这道题又是会更新到最后的了,不是中间某个数可能最大了。
因为一定要找尽 s 串,s 串的最后元素代表的一定是最大的。
而之前的题,??????????
哎卧槽晕了
我觉得这道题好难。
我觉得有必要画图模拟一下了。
画了,但还是没想明白。
或者可以这么想,比如s串遍历到了 ra, t这个时候是rab
a和b不相等
那么 ra 中有rab的个数就是 r 中 有 rab 的个数。这个就是模拟删除 s 中的元素。 那么为什么不模拟 删除 t 中的元素呢?
就是卡哥说的因为是找 s 中有多少个 t ,再结合 dp 数组的含义。s 到i-1 和 t 到j-1 了时,s中有多少个t。所以自然不用看删除 t 的话 ra中有多少个 ra(s的)
再举个例子
如果s遍历到了rab,t这个时候也是rab
b 和 b 相等
那么rab中有rab的个数就是 ra 中有ra的个数 + ra 中有rab的个数。
为什么要加上ra中有rab的个数呢?因为这里加的就是重复的情况。
比如 s遍历到rabb,t这个时候是rab。
因为b和b相等。那么rabb中有rab的个数就是 rab中有 ra的个数 + rab中有rab的个数。
哎目前我就通过这三个例子去理解,差不多吧,以后再悟。
反正就是通过这样的递推公式,在 i 循环的过程中,就能求出每个情况 s 里有多少个t。
然后 s 遍历完了,也就求出来 整个 s有多少个t了。
所以最后返回的也是 dp 数组的最后元素。
class Solution {
public int numDistinct(String s, String t) {
//dp
int[][] dp = new int [s.length() + 1][t.length() + 1];
//init
for (int i = 0; i < s.length() + 1; i++) {
dp[i][0] = 1;
}
//func
for (int i = 1; i < s.length() + 1; i++) {
for (int j = 1; j < t.length() + 1; j++) {
if (s.charAt(i - 1) == t.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[s.length()][t.length()];
}
}