leetcode 字符串動態規划總結


問題1:leetcode 正則表達式匹配

請實現一個函數用來匹配包括'.'和'*'的正則表達式。模式中的字符'.'表示任意一個字符,而'*'表示它前面的字符可以出現任意次(包含0次)。 在本題中,匹配是指字符串的所有字符匹配整個模式。例如,字符串"aaa"與模式"a.a"和"ab*ac*a"匹配,但是與"aa.a"和"ab*a"均不匹配

思路:

如果 pattern[j] == str[i] || pattern[j] == '.', 此時dp[i][j] = dp[i-1][j-1];

如果 pattern[j] == '*'
    分兩種情況:
    1: 如果pattern[j-1] != str[i] && pattern[j-1] != '.', 此時dp[i][j] = dp[i][j-2] //a*匹配0次
    2: 如果pattern[j-1] == str[i] || pattern[j-1] == '.'
        此時dp[i][j] = dp[i][j-2] // a*匹配0次
        或者 dp[i][j] = dp[i][j-1] // a*匹配1次
        或者 dp[i][j] = dp[i-1][j] // a*匹配多次
 public boolean match(char[] str, char[] pattern)
    {
         if(str == null || pattern == null)  return true;boolean [][] dp = new boolean[str.length+1][pattern.length+1];
        dp[0][0] = true;
        for(int i=1; i<dp[0].length;i++){
             if(pattern[i-1] == '*'){
                 dp[0][i] = dp[0][i-2];
             }
        }
        for(int i=1; i<dp.length; i++){
            for(int j=1;j<dp[0].length; j++){
                if(pattern[j-1] == '*'){
                    if(str[i-1] == pattern[j-2] || pattern[j-2] == '.'){
                        dp[i][j] = dp[i][j-2] || dp[i-1][j] || dp[i][j-1];
                        // 0  多次  一次
                    }
                    else{
                        dp[i][j] = dp[i][j-2];
                    }
                }
                else if(str[i-1] == pattern[j-1] || pattern[j-1] == '.'){
                        dp[i][j] = dp[i-1][j-1];
                }
            }
        }

        for(int i=0; i<dp.length; i++){
            for(int j=0; j<dp[0].length; j++){
                System.out.print(dp[i][j] + " ");
            }
            System.out.println();
        }
        return dp[dp.length-1][dp[0].length-1];
    }

 問題2:  leetcode 44 通配符匹配

給定一個字符串 (s) 和一個字符模式 (p) ,實現一個支持 '?' 和 '*' 的通配符匹配。

'?' 可以匹配任何單個字符。
'*' 可以匹配任意字符串(包括空字符串)。

兩個字符串完全匹配才算匹配成功。

思路: 定義dp[i][j]為s的前i個字符與p的前j個字符是否匹配,那么當p[j] 為‘*’的時候, dp[i][j] = dp[i][j-1] || (i >0 && (dp[i-1][j] || dp[i-1][j-1]) );// 匹配多次或者一次

            當p[j-1]為“?”時候,dp[i][j] = dp[i-1][j-1]

           

class Solution {
       public boolean isMatch(String s, String p) {
         int m = s.length(), n=p.length();
         boolean[][] dp = new boolean[m+1][n+1];
         dp[0][0] =  true;
//        String str = "adceb";
//        String pattern = "*a*b";
         for(int i = 0; i<=m; i++){
             for(int j=1;j<=n;j++){
                if( p.charAt(j-1)=='*'){
                      dp[i][j] = dp[i][j-1] || (i >0 && (dp[i-1][j] || dp[i-1][j-1]) );// 匹配多次或者一次
                }
                if( i>=1 && (p.charAt(j-1)=='?' || s.charAt(i-1) == p.charAt(j-1)) ){
                      dp[i][j] = dp[i-1][j-1];
                }
             }
         }

        // for(int i = 0; i<=m; i++){
        //     for(int j=0;j<=n;j++) {
        //       System.out.print(dp[i][j] + " ");
        //     }
        //     System.out.println();
        // }
         return dp[m][n];
    }
}

問題三:leetcode 97 交替字符串

給定三個字符串 s1, s2, s3, 驗證 s3 是否是由 s1 和 s2 交錯組成的。

示例 1:

輸入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac"
輸出: true

示例 2:

輸入: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc"
輸出: false

思路: 定義dp[i][j]為s1的前i個字符與s2的前j個字符是否可以組成s3的前i+j個字符,注意空字符串,所以s1與s2都是從1開始計數的
class Solution {
public boolean isInterleave(String s1, String s2, String s3) {
          if(s1.equals("")){
              return s2.equals(s3);
          }
          if(s2.equals("")){
              return s1.equals(s3);
          }

          int m = s1.length(), n = s2.length();
         if(m+n != s3.length()){
              return false;
          }
          boolean[][] dp = new boolean[m+1][n+1];
          dp[0][0] = true;

//        String s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac";

          for(int i=0; i<= m; i++){
              for(int j=0; j<=n;j++){
                  if(i==0 && j==0) continue;
                  dp[i][j] = (i>0 && s1.charAt(i-1)==s3.charAt(i+j-1) && dp[i-1][j]) ||
                             (j>0 && s2.charAt(j-1) == s3.charAt(i+j-1) && dp[i][j-1] );
              }
          }

        // for(int i=0; i<= m; i++){
        //     for(int j=0; j<=n;j++) {
        //        System.out.print(dp[i][j] +" ");
        //     }
        //     System.out.println();
        // }
        return dp[m][n];
    }
}

leetcode 不同子序列 115

給定一個字符串 和一個字符串 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

 思路1: 定義 dp[i][j] 為s[i]等於t[j]的子序列數目:dp[i][j] += dp[k][j-1]; s[k] = t[j-1];

class Solution {
   public int numDistinct(String s, String t) {
        // S = "rabbbit", T = "rabbit"

//        String S = "babgbag", T = "bag";
        int m = s.length(), n = t.length();
        int[][] dp = new int[m][n];
        // dp[i][j] =  dp[k][j-1]  s[k] == t[j]
        for(int i=0; i<m;i++){
            dp[i][0] = s.charAt(i) == t.charAt(0) ? 1 : 0;
        }
        for(int i=0; i<m;i++){
            for(int j=0; j<n;j++){
                if(s.charAt(i) == t.charAt(j)) {
                    for (int k = 0; k < i; k++) {
                        if (j>0 && s.charAt(k) == t.charAt(j-1)) {
                            dp[i][j] += dp[k][j-1];
                        }
                    }
                }
            }
        }
        // for(int i=0; i<m;i++) {
        //     for (int j = 0; j < n; j++) {
        //       System.out.print(dp[i][j]+" ");
        //     }
        //     System.out.println();
        // }
        int result = 0;
        for(int i=0 ; i<m;i++){
             result += dp[i][n-1];
        }
        return result;
    }
}

思路2:

而是判斷S有多少種方式可以得到T。但其實還是動態規划,我們一個定義二維數組dp,dp[i][j]為字符串s(0,i)變換到t(0,j)的變換方法的個數。

如果S[i]==T[j],那么dp[i][j] = dp[i-1][j-1] + dp[i-1][j]。意思是:如果當前S[i]==T[j],那么當前這個字符即可以保留也可以拋棄,所以變換方法等於保留這個字符的變換方法加上不用這個字符的變換方法, dp[i-1][j-1]為保留這個字符時的變換方法個數,dp[i-1][j]表示拋棄這個字符時的變換方法個數。
如果S[i]!=T[i],那么dp[i][j] = dp[i-1][j],意思是如果當前字符不等,那么就只能拋棄當前這個字符。

class Solution {
    public int numDistinct(String s, String t) {
        int[][] res = new int[s.length()+1][t.length()+1];
        res[0][0] = 1;
        for(int i=1;i<t.length()+1;i++)
            res[0][i] = 0;
        for(int i=1;i<s.length()+1;i++)
            res[i][0] = 1;
        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)){
                    res[i][j] = res[i-1][j-1] + res[i-1][j];
                }
                else{
                    res[i][j] = res[i-1][j];
                }
            }
        return res[s.length()][t.length()];
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM