算法-两个字串差异性问题

这类问题都可以借助动态规划实现,其中字符串1作为x轴,字符串2作为y轴

精要概括一下,就是两个字符串支持四种操作,分别是添加,修改,删除,不变

1、编辑距离问题

72. 编辑距离

给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符
 

示例 1:

输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:

输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

借助这张图可以更好理解:
在这里插入图片描述

编辑距离支持插入删除和替换操作,我们建立动态规划方程dp[i][j]来表示字符串在第i-1和第j-1处的匹配情况,可以分为两种情况

cs1[i-1]==cs2[j-1],表明字符串匹配,不需要编辑,那么:

dp[i][j]=dp[i-1][j-1]

cs1[i-1]!=cs2[j-1],需要执行插入删除替换操作,究竟哪一种操作好呢?我们应当计算最小值,再+1步。

dp[i][j]=Math.min(dp[i-1][j-1],Math.min(dp[i-1][j],dp[i][j-1]))+1;

那动态规划方程的初始状态如何确定呢?
我们知道从空字串变到某个字串只有一种可能性,就是添加字符,所以,数组第一行和第一列都等于对应的行列值。

...
dp[i][0]=i;
...
dp[0][j]=j;
...

至此问题就迎刃而解了:

    public int minDistance(String word1, String word2) {
        char[] cs1=word1.toCharArray();
        char[] cs2=word2.toCharArray();
        int[][] dp=new int[cs1.length+1][cs2.length+1];
        for(int i=0;i<=cs1.length;i++){
            dp[i][0]=i;
        }
        for(int j=0;j<=cs2.length;j++){
            dp[0][j]=j;
        }

        for(int i=1;i<=cs1.length;i++){
            for(int j=1;j<=cs2.length;j++){
                if(cs1[i-1]==cs2[j-1]){
                    dp[i][j]=dp[i-1][j-1];
                }else{
                    dp[i][j]=Math.min(dp[i-1][j-1],Math.min(dp[i-1][j],dp[i][j-1]))+1;
                }
            }
        }
        return dp[cs1.length][cs2.length];
    }

2、最长公共子序列问题

1143. 最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

示例 1:
输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace",它的长度为 3。
示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3。
示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0。
 

提示:

1 <= text1.length <= 1000
1 <= text2.length <= 1000
输入的字符串只含有小写英文字符。

下面的图是从别的地方找来的,借以说明本问题的解决方案。与编辑距离不同的是,本题目中不能添加字符,只能删减字符,所以数组的第一行和第一列都为0,而编辑距离中由于可以添加字串,所以第一行第一列都为1(除0,0位置元素)。
在这里插入图片描述
本题中只有删除操作

设dp[i][j]为字符串在i位置和j位置的最长字串长度,那么可以看出,如果cs[i-1]==cs[j-1],那么位置(i,j)的值dp[i][j]=dp[i-1][j-1]+1
否则的话dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])。也就是删除一个字符

由此我们可以得出结论

    public int longestCommonSubsequence(String text1, String text2) {
        char cs1[]=text1.toCharArray();
        char cs2[]=text2.toCharArray();
        int[][] dp=new int[cs1.length+1][cs2.length+1];
        for(int i=1;i<=cs1.length;i++){
            for(int j=1;j<=cs2.length;j++){
                if(cs1[i-1]==cs2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return dp[cs1.length][cs2.length];
    }

3、不同的子序列

115. 不同的子序列

给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。

一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)

 

示例 1:

输入:S = "rabbbit", T = "rabbit"
输出:3
解释:

如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。
(上箭头符号 ^ 表示选取的字母)

rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
示例 2:

输入:S = "babgbag", T = "bag"
输出:5
解释:

如下图所示, 有 5 种可以从 S 中得到 "bag" 的方案。 
(上箭头符号 ^ 表示选取的字母)

babgbag
^^ ^
babgbag
^^    ^
babgbag
^    ^^
babgbag
  ^  ^^
babgbag
    ^^^

这题与上面两题如出一辙,只不过条件改成了只能删除或者不删除。

不删除是什么情况呢?

dp[i][j]=dp[i-1][j-1];

删除是什么情况呢?

p[i][j]=dp[i][j-1];

如果对应两个位置的字符相同,我们选择删除或者不删除都可以,所以:

dp[i][j]=dp[i-1][j-1]+dp[i][j-1];
    public int numDistinct(String s, String t) {
        int len1=s.length();
        int len2=t.length();
        int [][]dp=new int[len2+1][len1+1];
        for(int i=0;i<=len1;i++){
            dp[0][i]=1;//第一行为1,因为删除字符得到空字符
        }
        char[] cs=s.toCharArray();
        char[] ct=t.toCharArray();
        for(int i=1;i<=len2;i++){
            for(int j=1;j<=len1;j++){
                if(cs[j-1]==ct[i-1]){
                    dp[i][j]=dp[i-1][j-1]+dp[i][j-1];
                }else{
                    dp[i][j]=dp[i][j-1];
                }
            }
        }
        return dp[len2][len1];
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章