動態規劃案例解析

此文總結了本人在刷LeetCode是遇到的動態規劃問題以及對應的解法。

word-break

Given a string s and a dictionary of words dict, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
For example, given
s =”leetcode”,
dict =[“leet”, “code”].
Return true because”leetcode”can be segmented as”leet code”.

給定一個字符串S以及一個字典dict,判斷S是否可以由dict組成。
解題思路:使用動態規劃,將字符串分割S,使用dp[i]來表示字符串的前i個字符組成的是否可以由dict組成。使用dp[j]表示前j個字符是否可由dict組成,且j小於i。此時可得狀態轉移方程:
dp[i] = dp[j]&&(字符串S從j→i組成的字符爲單詞)。
舉例題描述作爲例子:
dp[8] = dp[4]&&(字符串S從5→8組成的字符是code爲單詞)。
dp[4] = dp[0]&&(字符串S從1→4組成的字符是leet爲單詞)。
dp[0]我們默認爲true。

java的AC代碼如下:

public boolean wordBreak(String s, Set<String> dict) {
        //使用d[i]表示s[0-i]是否可以匹配
        //d[i] = d[j]&&(s[j+1-i]也是單詞)
        //j表示分詞座標,0<j<i.
        //以leetcode爲例
        //d[8]=d[4]&&(s[5-8]=code也是單詞) 所以爲true
        //d[4] = d[0]&&(s[1-4]=leet也是單詞) 所以爲true
        int len = s.length();
        boolean[] dp = new boolean[len+1];
        dp[0] = true;
        for(int i=1;i<=len;i++){
            for(int j=0;j<=i;j++){
                //如果前面j個可由字典組成,並且j→i也是單詞
                if(dp[j]==true&&dict.contains(s.substring(j,i))){
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[len];
    }

palindrome-partitioning

Given a string s, partition s such that every substring of the partition is a palindrome.
Return the minimum cuts needed for a palindrome partitioning of s.
For example, given s =”aab”,
Return1since the palindrome partitioning[“aa”,”b”]could be produced using 1 cut.
給定一個字符串S,我們需要將S分割成一個一個迴文的字符串,需要分割多少次?

解題思路:假設dp[i]表示字符串前i個(包括i)個字符串需要的分割次數。
1.如果字符串前i個形成的子串是迴文,那麼dp[i] = 0;f否則,我們先假設dp[i] = i-1,取最壞的情況;
2.設一個變量j,j小於i,如果S[j→i]的子串是迴文,那麼dp[i] = min(dp[i], dp[j-1]+1);
3.如果S[j→i]的子串不是迴文,那麼dp[i] = min(dp[i], dp[j-1]+1+i-j),dp[j-1]+1+i-j中的(i-j)表示對於S[j→i]我們需要分割i-j次,使其編程單個字符。

java的AC代碼如下:

public int minCut(String s) {
        int len = s.length();
        int[] dp = new int[len];
        for(int i=0;i<len;i++){
            dp[i] = isPalindrome(s.substring(0,i+1))==true? 0:i;
            for(int j=1;j<=i;j++){
                if(isPalindrome(s.substring(j,i+1)))//如果j→i字符串是迴文
                    dp[i] = Math.min(dp[i],dp[j-1]+1);
                else
                    dp[i] = Math.min(dp[i],dp[j-1]+i-j+1);
            }
        }
        return dp[len-1];
    }
    //判斷字符串是否爲迴文
    public boolean isPalindrome(String s){
      for(int i=0,j=s.length()-1;i<j;i++,j--){
          if(s.charAt(i) != s.charAt(j))
              return false;
      }
      return true;
  }

distinct-subsequences

Given a string S and a string T, count the number of distinct subsequences of T in S.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie,”ACE”is a subsequence of”ABCDE”while”AEC”is not).
Here is an example:
S =”rabbbit”, T =”rabbit”
Return3.

通過刪除字符(可以不刪除),使得S變爲T,計算出有幾種不同的刪除方法。

解題思路:
1.使用dp[i][j]表示將S.substring(0,i)變爲T.substring(0,j)的方法數。
2.我們知道任何一個字符串變爲空,只有一種方法,所以dp[i][0] = 1;
3.如果S的i字符和T的j字符相同,那麼就有兩種策略,如果不刪除,那麼dp[i][j] = dp[i-1][j-1];如果刪除那麼dp[i][j] = dp[i-1][j]
4.如果S的i字符和T的j字符不相同,那麼只能講S的i字符刪除,dp[i][j] = dp[i-1][j];

java的AC代碼:

public int numDistinct(String S, String T) {
        if(S==null||T==null)
            return 0;
        if(S.length()<T.length())
            return 0;
        int[][] dp = new int[S.length()+1][T.length()+1];
        dp[0][0] = 1;
        //任何一個字符串變爲空只有一種方法
        for (int i = 1; i <S.length(); i++) {
            dp[i][0] = 1;
        }
        for (int i = 1; i <= S.length(); i++) {
            for (int j = 1; j <= T.length(); j++) {
                dp[i][j] = dp[i-1][j];
                if(S.charAt(i-1)==T.charAt(j-1))
                    dp[i][j] += dp[i-1][j-1];
            }
        }
        return dp[S.length()][T.length()];
    }

minimum-path-sum

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.

給定一個充滿非負數的m×N網格,從左上角到右下角找到一條路徑,它沿着路徑將所有數的總和最小化。

解題思路:我們這裏假設dp[i][j]表示從原點到(i,j)所有的數總和。
那麼狀態轉移方程可以表示爲dp[i][j] = min(dp[i][j-1],dp[i-1][j])+點(i,j)的數;
並且dp[i][0] = dp[i-1][0]+點(i,0)的數;dp[0][j] = dp[0][j-1]+點(0,j)的數;

java的AC代碼如下:

public int minPathSum(int[][] grid) {
        if (grid == null)
            return 0;
        int[][] dp = new int[grid.length+1][grid[0].length+1];
        dp[0][0] = grid[0][0];
        for(int i=1;i<grid.length;i++)
            dp[i][0] = dp[i-1][0]+grid[i][0];//第一行初始化
        for(int j=1;j<grid[0].length;j++)
            dp[0][j] = dp[0][j-1]+grid[0][j];//第一列初始化
        for(int i=1;i<grid.length;i++){
            for(int j=1;j<grid[0].length;j++)
                dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
        }
        return dp[grid.length-1][grid[0].length-1];
    }

edit-distance

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)
You have the following 3 operations permitted on a word:
a) Insert a character
b) Delete a character
c) Replace a character

給定兩個字符串word1 and word2,求出從word1轉化爲word2需要的最小的步驟。
下列操作表示一步:
a)插入字符
b)刪除字符
c)替換字符

解題思路:使用dp[i][j]表示word1的前i個轉化爲word2的前j個字符組要的最小步驟。
1.任何字符串轉化爲空的步驟=非空字符串的字符個數,也就是dp[i][0] = i;dp[0][j] = j;
2.如果word1.charAt(i) == word2.charAt(j ),那麼此時步驟等同於dp[i][j] = dp[i-1][j-1],abc,dfc這種情況與ab,df花費是一樣的
3.如果word1.charAt(i) != word2.charAt(j ),那麼就取刪、改、增三種操作最少的情況再+1。

AC代碼:

public int minDistance(String word1, String word2) {
        if(word1==null&&word2==null) return 0;
        if(word1==null||word1.length()==0) return word2.length();
        if(word2==null||word2.length()==0) return word1.length();
        int[][] dp = new int[word1.length()+1][word2.length()+1];
        dp[0][0] = 0;
        //初始化dp
        for(int i=1;i<=word1.length();i++) dp[i][0] = i;
        for(int j=1;j<=word2.length();j++) dp[0][j] = j;
        for(int i=1;i<=word1.length();i++){
            for(int j=1;j<=word2.length();j++){
                if(word1.charAt(i-1)==word2.charAt(j-1)) 
                    dp[i][j] = dp[i-1][j-1];
                else 
                     // 要在刪除,插入,修改中取花費最小的那個
                    dp[i][j] = Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
            }
        }
        return dp[word1.length()][word2.length()];
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章