算法-兩個字串差異性問題

這類問題都可以藉助動態規劃實現,其中字符串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];
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章