动态规划之子序列,还是为了编辑距离做铺垫 | LeetCode:583.两个字符串的删除操作_哔哩哔哩_bilibili 动态规划终极绝杀! LeetCode:72.编辑距离_哔哩哔哩_bilibili
583. 两个字符串的删除操作
思路:返回使得s与t相同所需要删除的字符数,这道题初始的处理思想就是转换为求s与t的共同子序列的长度,然后返回 s的长度-共同子序列的长度+t的长度-共同子序列的长度 即可。
// 时间复杂度O(n^2)
// 空间复杂度O(n^2)class Solution {public int minDistance(String word1, String word2) {// 使得word1相同,那么说明就是找两个字符串中的非连续最长公共序列// 并且是两个字符串内的元素都可以删除,与以往在s中找t的递推方程不同// 在s中找t是s中必定存在t,且存在若干t内的元素的重复项,且只可以删除s中的元素,因此我们递推时,dp[i][j]需要考虑与t中每一个元素重复的可能// 但是仅仅是比较两个字符串中的元素交集,那么每个元素之间的匹配其实应该是唯一的,即s中只会存在一个t,这点在题目中其实是隐晦的,是在经历过s找t才形成的int n = word1.length();int m = word2.length();char[] s1 = word1.toCharArray();char[] s2 = word2.toCharArray();int[][] dp = new int[n+1][m+1];// 默认dp[0][0]就是零for(int i=1; i<=n; i++){for(int j=1; j<=m; j++){if(s1[i-1] == s2[j-1])dp[i][j] = dp[i-1][j-1]+1;elsedp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);}}return (n-dp[n][m])+(m-dp[n][m]);}
}
72. 编辑距离
思路:注意理解编辑距离所求解的是使得s与t成为相同字符串所进行的操作次数,删除s的字符,删除t的字符,替换s和t的字符都属于操作了一次。而dp[i][j]与题目意思保持一致,所具备的含义就是s的i位置前与t的j位置前的字符串称为相同字串需要编辑多少次。所以不管是dp[i-1][j],dp[i][j-1],dp[i-1][j-1]都是属于对s或者t进行了一次编辑。
其次对于序列题目,暴力的思路都是两个字符串内字符一一比较,所以字符串题目的关键就是两个任意位置的字符进行比较。所以在字符题,用动态规划求解的话,发生转移的条件就是判断s[i]与t[j]的相等或不相等。
本题dp[i-1][j],dp[i][j-1]都是删除操作,dp[i-1][j-1]与dp[i][j]而言是替换操作;因为这个替换体现在dp[i][j]会得到dp[i-1][j-1]+1,即我默认通过替换一个元素使得s[i]与t[j]相等了,那么自然转移到的前一状态就是dp[i-1][j-1]。其次对于增加操作,可以等效于在另一者上执行删除,这点需要多理解,我存在的疑惑就是如果可以采用删除来替换增加的话,那么是否会影响之后的元素的判断。
// 时间复杂度O(n^2)
// 空间复杂度O(n^2)class Solution {public int minDistance(String word1, String word2) {int n = word1.length();int m = word2.length();char[] w1 = word1.toCharArray();char[] w2 = word2.toCharArray();int[][] dp = new int[n+1][m+1];// 初始化,初始化是针对一方是空串,因此所进行的操作是删除,赋值的内容就是删除的元素for(int i=0; i<=n; i++)dp[i][0] = i;for(int j=0; j<=m; j++)dp[0][j] = j;for(int i=1; i<=n; i++){for(int j=1; j<=m; j++){if(w1[i-1] == w2[j-1])dp[i][j] = dp[i-1][j-1]; // w1与w2中出现相同的元素,则无需进行处理if(w1[i-1] != w2[j-1])dp[i][j] = Math.min(Math.min(dp[i-1][j-1], dp[i-1][j]), dp[i][j-1])+1; // dp[i-1][j]是删除 dp[i][j-1]是增加,相当于重新遍历当前j的位置,所以加1}}return dp[n][m];}
}