動態規劃第五講——leetcode上的題目動態規劃匯總(上)

來源:互聯網
上載者:User

標籤:動態規劃   leetcode   

本節,我們將對leetcode上有關DP問題的題目做一個匯總和分析。

1.題目來源Interleaving String 動態規劃 二叉樹
Unique Binary Search Trees 動態規劃二叉樹
Word Break 動態規劃N/A
Word Break II 動態規劃N/A
Palindrome Partitioning 動態規劃N/A
Palindrome Partitioning II 動態規劃N/A
Triangle 動態規劃 N/A
Distinct Subsequences 動態規劃N/A
Decode Ways 動態規劃N/A
Scramble String 動態規劃N/A
Maximal Rectangle 動態規劃N/A
Edit Distance 動態規劃N/A
Climbing Stairs 動態規劃N/A
Minimum Path Sum 動態規劃N/A
Unique Paths 動態規劃N/A
Unique Paths II 動態規劃N/A
Jump Game 動態規劃N/A
Jump Game II 動態規劃N/A
Maximum Subarray 動態規劃N/A
Wildcard Matching 動態規劃N/A
Substring with Concatenation of All Words 動態規劃N/A
2.題目分析 Eg1:word break

我們定義bool dp[i+1]為s(0~i)是否可以被分解,這樣就能很容易得到遞推式並求解


Eg2:wor break2:

相對上一題目而言,這道題目的結果變成了一個全路徑的搜尋問題;不僅要判斷s是否可以分解,還要給出所有分解的方案.這算是DP問題的一種很典型的形式。如果我們用vector<vector<string> > dp[i+1]來儲存解決方案,這樣會導致空間的溢出O(n^3).所以,改進一下,這裡我們並不儲存全路徑,而是儲存部分路徑。vector<int>  dp[i+1], 能夠匹配到dp[i+1]的上一個下標,這樣空間複雜度是O(n^2).


分析2:第一步中,我們得到了一個有向非循環圖,現在的任務就是遍曆這個圖,求出所有的路徑。這類似於一個普通樹的深度優先搜尋。代碼如下
class Solution {                                                                                                                                     public:             vector<string> wordBreak(string s, unordered_set<string> &dict) {        VS result;                                           int n=s.size();if(n==0){result.push_back("");return result;}VI test;//dp[i]from 0 to n: the last part of pathVVI dp( n+1, test);int i, t;for (i = 0; i < n; ++i){for (t = 0; t <i+1; ++t){if(t==0 || dp[t].size()!=0){//s[t-1] is ok!if(dict.find(s.substr(t, i+1 - t)) != dict.end()){dp[i+1].push_back(t);//forcus}}}}string path;if(dp[n].size()!=0)    result = bfs(n, dp, path, s);return result;}VS bfs(int index, VVI const &dp, string path, string const &s){VS result;if(dp[index].size()==0){result.push_back(path.substr(1, path.length()));return  result;}int n= dp[index].size();int i;for (i = 0; i < n; ++i){VS pre;pre = bfs(dp[index][i], dp, " "+ s.substr(dp[index][i], index - dp[index][i])+path, s ) ;int size_pre = pre.size();int j;for(j=0; j<size_pre; j++){result.push_back(pre[j]);}}return result;}};


Eg 3:Palindrome Partitioning動態規劃 N/A


分析,這道題目和上一道有點類似,也是一個基於動態規劃的全路徑搜尋問題。首先使用DP計算s[i,j]是否是迴文;最後當然要加上一個步驟:採用dfs搜尋全路徑.
代碼如下:
class Solution {public:      vector<vector<string>> partition(string s) {        int n = s.length();        vector<vector<string> > res;        vector<string> each;        if(n < 2) {            each.push_back(s);            res.push_back(each);            return res;        }               /* get state[i][j] */        vector<bool> done(n, false);        vector<vector<bool> > state(n, done);        get_palindron_state(s, state);        vector<string> pathed;        dfs(s, state, n-1, pathed, res);        return res;    }               void get_palindron_state(string s, vector<vector<bool> > &state){        int n = state.size();        for (int i = n-1; i >=0 ; --i){            for (int j = i; j < n; ++j){                if(i==j) state[i][j]=true;                else{                    if(s[i] == s[j]){                        if(i+1==j) state[i][j]=true;                        else state[i][j]= state[i+1][j-1];                    }                }            }        }    }              void dfs(const string &s,const vector<vector<bool> > &state,  int end, vector<string>  pathed, VVS &res ){        for (int i = end; i >= 0; --i){                 if(state[i][end]){                   pathed.insert(pathed.begin(), s.substr(i, end-i+1));                dfs(s,state,  i-1, pathed, res);                pathed.erase(pathed.begin());            }                 }                     if(end == -1){                       res.push_back(pathed);        }    }     };  
    

Eg4:Palindrome Partitioning II


我們先求出state[i][j]:s(i, j)是否是迴文;定義dp[i+1]表示s(i, s.length()-1)的最小分割數,最後dp[0]-1就是我們需要的結果。

dp[i] = min{dp[j+1] + 1| s(i, j)是迴文}


Eg5:Triangle


太典型了,不講。


Eg6:,Distinct Subsequences


分析:這道題目,可能初看之下,不能很好的想到使用動態規劃求解—— 這是一個雙序列類型的題目,熟練了以後就比較好往DP思考了。現在來看一下分析過程。


要求:numDistinct(string S, string T), if(S[0]!=T[0]) res = numDistinct(S(1, @), T);if(S[0]==T[0]) res= numDistinct(S(1, @), T(1, @))+numDistinct(S(1, @), T);通過分治法,我們已經發現了題目的顯著特徵:重疊子問題和最優子結構。接下來就是構造DP的問題了。
dp[i][j]:s(0,i), T(0,j)的numDistinct的數目,於是dp[i][j]= dp[i-1][j] + dp[i-1][j-1](if(S[i]==S[j]))

還是那句話:DP問題的痛點不在於構造而在於識別。如果不能一眼看出DP問題的特徵,那麼就老老實實按照:問題是否可分;分解後子問題是否重疊;重疊的子問題是否滿足最優子結構來判定。

class Solution {               public:                            int numDistinct(string S, string T) {        int i, j;                      int m= S.length(), n=T.length();        int dp[m][n];                  if(m==0 || n==0 ) return 0;        fill(dp[0], dp[0]+n, 0);         if(S[0]==T[0]) dp[0][0]=1;        for (i = 1; i < m; ++i){            dp[i][0]=dp[i-1][0];            if(S[i]==T[0]) dp[i][0]++;        }                                                             for (i = 1; i < m; ++i){            for(j=1; j<n; j++){                dp[i][j] = dp[i-1][j];                if(S[i]== T[j]) dp[i][j] += dp[i-1][j-1];            }                          }                              return dp[m-1][n-1];                                                                                                                             }                          };  

Eg7:Decode Ways 
分析:比較簡單,如果不熟練可以先寫出遞迴形似
class Solution {public:                    int numDecodings(string s) {        int n=s.length();        if(n==0 ) return 0;        int i;          int dp[n+1];        fill(dp, dp+n+1, 0);        dp[0]=1;                                                                                                                                             if(isdigit(s[0] ) && s[0]!='0') dp[1]= 1;        else dp[1]=0;        if(n==1) return dp[1];        for (i = 1; i < n; ++i){            if(isdigit(s[i]) && s[i]!='0') dp[i+1] += dp[i];            if(s.substr(i-1, 2) >= string("10") && s.substr(i-1, 2) <= string("26")) dp[i+1] += dp[i-1];        }               return dp[n];    }               };
   

Eg8:Scramble String 


分析,很顯然,這又是一個雙序列問題。但是,這道題目卻並不是那麼容易分析,因為組成一個問題的子問題多而複雜—— 這也是DP的痛點所在。
isScramble(string s1, string s2){
int n=s1.length();
for (int i = 1; i < n; ++i){//len
res ||= isScramble(s1.first, s2.first) && isScramble(s1.second, s2.second)
|| isScramble(s1.first, s2.second) && isScramble(s1.second, s2.first);
//為了方便,我們用firt代表s1的前半部分,
if(res = true) return true;
}
return false;
}

這是這個問題的遞迴解法,也是最直觀的解法;首先,子問題可分是肯定的;另外,通過對子問題的觀察,我們發現的確有子問題重疊現象,現在就剩下如何定義DP了。


直觀的反應是這樣定義:dp[i1][i2][j1][j2]:s(i1,i2), s2(j1, j2)是isScramble?但是這裡有一個隱含的條件,如果是true的話,i2-i1 == j2-j1;於是,我們重新定義dp[i][j][l]:s1從i開始,s2從j開始,長度為l的字串是否滿足Scramble。具體實現的代碼如下:
class Solution {                                                                                                                                     public:                                    bool isScramble(string s1, string s2) {                                                                        int n1=s1.length();                    int n2=s2.length();                    if(n1!=n2)                                 return false;                      bool dp[n1][n1][n1+1];        memset(dp, false, sizeof(dp));        /* dp[i][j][k]  start of s1, j:start of s2, k len of them */                int i, j, k, l;        for (i = 0; i < n1; ++i){            for (j = 0; j < n1; ++j){                dp[i][j][1] = (s1[i]==s2[j]);            }          }           for(k=2; k<=n1;k++){            for(i=0; i+k<=n1; i++){                for (j = 0; j+k <= n1; ++j){                    for(l=1; l<k; l++){                        if( (dp[i][j][l] && dp[i+l][j+l][k-l]) || (dp[i][j+k-l][l] && dp[i+l][j][k-l])){                            dp[i][j][k] = true;                            break;                        }                       }                   }               }          }           return dp[0][0][n1];    }                                  }; 

Eg9:Maximal Rectangle


這道題,DP是其中一個比較小的環節,使用者資料預先處理,dp[i][j]第i行j列上的元素往上延伸的1的個數。剩下的,就是另外一道題目的變形:最大長條圖。


class Solution {   public:          int maximalRectangle(vector<vector<char> > &matrix) {              int row_len = matrix.size();        if(row_len == 0) return 0;        int col_len = matrix[0].size();        if(col_len ==0 ) return 0;        vector<int> height(col_len, 0);        vector<int> lastheight(col_len, 0);        int res=0;                 for (int i = 0; i < row_len; ++i){            for( int j=0; j<col_len ; j++){                if(matrix[i][j] == '1'){                    height[j]=lastheight[j]+1;                }                else                    height[j]=0;            }                  res = max(res, largest(height));            lastheight = height;        }        return res;    }         int largest(vector<int> height) {           height.push_back(0);// be cautious        stack<int> stk;          int i = 0;          int maxArea = 0;          while(i < height.size()){              if(stk.empty() || height[stk.top()] <= height[i]){                  stk.push(i++);              }else {                   int t = stk.top();                  stk.pop();                  maxArea = max(maxArea, height[t] * (stk.empty() ? i : i - stk.top() -1));              }        }        return maxArea;      }   };   

Eg9:Edit Distance


這也是一個典型的雙序列問題,分析方法同上,代碼如下

class Solution {

public:    int minDistance(string word1, string word2) {        int len1 = word1.length(), len2 = word2.length();        int dp[len1+1][len2+1];        for (int i = 0; i < len1+1; ++i){            dp[i][0]=i;        }           for (int i = 0; i < len2+1; ++i){            dp[0][i]=i;        }           int i, j;        for (i = 0; i < len1; ++i){            for (j = 0; j < len2; ++j){                if(word1[i] == word2[j]) dp[i+1][j+1] = dp[i][j];                else {                    dp[i+1][j+1] = min(dp[i][j], min(dp[i+1][j], dp[i][j+1])) +1 ;                }               }           }           return dp[len1][len2];    }   };   

(未完待續)

動態規劃第五講——leetcode上的題目動態規劃匯總(上)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.