Leetcode: Palindrome Partitioning II

Avril發表於2013-08-31

參考:http://www.cppblog.com/wicbnu/archive/2013/03/18/198565.html

我太喜歡用dfs和回溯法了,但是這些暴力的方法加上剪枝之後複雜度依然是很高,顯然不能達到題目的要求。

這個時候應該考慮動態規劃,並且要複雜度儘量接近O(n^2)的演算法。

下面這個方法更加簡潔:自長到短找到迴文串後,往後dfs,並記錄遞迴深度表示並更新最小劃分數。http://fisherlei.blogspot.com/2013/03/leetcode-palindrome-partitioning-ii.html


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",
Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut.

題解:
類似矩陣連乘的動歸思路。
dp[i][j]=min(dp[i][k]+dp[k+1][j]+1), i<=k<j.
但是用這個方程的時間是O(n^3),簡化dp[i][j]為dp[i],表示從0到i的minCut.
dp[i]=min(dp[k]+1,       dp[k]+i-k), 0<=k<i.

 (s[k+1, i]是迴文串)   (s[k+1, i]不是迴文串)

具體程式碼參見上述連結。

值得注意的是,計算是否為迴文數的過程中也要用記憶化搜尋才能減少重複比較的次數,it's smart~

 MY CODE:

  1 //
  2 //  ParlindromePartitioningII.cpp
  3 //  SJMcode
  4 //
  5 //  Created by Jiamei Shuai on 13-8-31.
  6 //  Copyright (c) 2013年 Jiamei Shuai. All rights reserved.
  7 //
  8 
  9 #include <vector>
 10 #include <iostream>
 11 #include <string.h>
 12 #include <assert.h>
 13 using namespace std;
 14 
 15 // 兩處優化:
 16 // 1.已經計算過的區間的最短劃分次數用map紀錄
 17 // 2.迴文串的判斷結果也要用map記錄
 18 
 19 class Solution{
 20 public:
 21     int *minCutMat;
 22     vector<vector<int> > map;
 23     
 24     int IsPalindrome(string &s, int i, int j)
 25     {
 26         if(i>j) return false;
 27         if(map[i][j]!= -1)
 28             return map[i][j];
 29         if(i==j)
 30             return map[i][j]=1;
 31         
 32         if(s[i]!=s[j])
 33             return map[i][j]=0;
 34         else{
 35             if(j-i==1)
 36                 return map[i][j]=1;
 37             else
 38                 return map[i][j]=IsPalindrome(s,i+1,j-1);
 39         }
 40     }
 41     
 42     
 43     int minCut(string s) // 動態規劃  d[i] = min{d[k]+1, d[k]+i-k}, 0<=k<i
 44     {
 45         int n = (int)s.length();
 46         if(n==0||n==1)
 47             return 0;
 48         
 49         vector<int> min, vtmp;
 50         min.clear();vtmp.clear();map.clear();
 51         for(int i=0; i<s.size(); i++)
 52         {
 53             min.push_back(0);
 54             vtmp.push_back(-1);
 55         }
 56         for(int i=0; i<s.size(); i++)
 57             map.push_back(vtmp);
 58 
 59         int tmp, ans;
 60         for(int inter = 1; inter<n; inter++)
 61         {
 62             if(IsPalindrome(s, 0, inter))
 63                 min[inter]=0;
 64             else{
 65                 ans = n+1;
 66                 for(int k = 0; k < inter; k++)
 67                 {
 68                     if(IsPalindrome(s, k+1, inter))
 69                         tmp = min[k]+1;
 70                     else
 71                         tmp = min[k] + inter - k;
 72                     if(tmp < ans)
 73                         ans = tmp;
 74                 }
 75                 min[inter] = ans;
 76             }
 77         }
 78         return min[n-1];
 79     }
 80     
 81     
 82     
 83     // 較複雜的演算法用dfs或者回溯法都太慢了,加上了所有的剪枝策略還是會超時
 84     // 這種情況大多數都應該使用動態規劃,要多總結,少犯錯誤。
 85     
 86     int minCut2(string s) // 總是超時,複雜度太高
 87     //這個方法相當於類似矩陣鏈乘的演算法,dp[i][j] = min(dp[i][k]+dp[k+1][j]), i<=k<j,複雜度是O(n^3)
 88     //可以簡化dp[i][j]為dp[i],表示從0到i的minCut
 89     {
 90         int minCutNum = (int)s.size();
 91         int len = (int)s.size();
 92         
 93         minCutMat = new int[len*len]; // 注意new int[]而不是()
 94         memset(minCutMat, -1, len*len*sizeof(int));
 95         
 96         vector<int> vtmp;
 97         vtmp.clear();map.clear();
 98         for(int i=0; i<s.size(); i++)
 99             vtmp.push_back(-1);
100         for(int i=0; i<s.size(); i++)
101             map.push_back(vtmp);
102         
103         // Notice: if the string need no split and itself a palindrome, how to handle it? 注意細節
104         if(IsPalindrome(s, 0, len-1)) return 0;
105                 
106         split(s, 0, len-1, minCutNum);
107         
108         delete []minCutMat;
109         
110         return minCutNum;
111     }
112     
113     int split(string &s, int begin, int end, int &minCutNum)
114     {
115         if(begin == end) return 0;
116         
117         if(IsPalindrome(s, begin, end)) return 0;
118         
119         int minCurrentSplit = (int)s.size();
120         int left,right;
121         
122         for(int i = begin; i < end; i++)
123         {
124             assert(begin*s.size()+i < s.size()*s.size());
125             assert(begin*s.size()+i < s.size()*s.size());
126             if(minCutMat[begin*s.size()+i] >= 0)
127                 left = minCutMat[begin*s.size()+i];
128             else
129             {
130                 left = split(s, begin, i, minCutNum);
131                 minCutMat[begin*s.size()+i] = left;
132             }
133             if(left >= minCutNum) { return 1<<20;}
134             
135             if(minCutMat[(i+1)*s.size()+end] >= 0)
136                 right = minCutMat[(i+1)*s.size()+end];
137             else
138             {
139                 right = split(s, i+1, end, minCutNum);
140                 minCutMat[(i+1)*s.size()+end] = right;
141             }
142             if(right >= minCutNum) return 1<<20;
143             
144             int tmp = left + 1 + right;
145             
146             minCurrentSplit = min(tmp, minCurrentSplit);
147             
148             if(begin == 0 && end == s.size()-1) // outer loop
149                 minCutNum = min(tmp, minCutNum);
150         }
151         return minCurrentSplit;
152     }
153 
154 };
155 
156 int main()
157 {
158     Solution sln;
159     cout << sln.minCut("apjesgpsxoeiokmqmfgvjslcjukbqxpsobyhjpbgdfruqdkeiszrlmtwgfxyfostpqczidfljwfbbrflkgdvtytbgqalguewnhvvmcgxboycffopmtmhtfizxkmeftcucxpobxmelmjtuzigsxnncxpaibgpuijwhankxbplpyejxmrrjgeoevqozwdtgospohznkoyzocjlracchjqnggbfeebmuvbicbvmpuleywrpzwsihivnrwtxcukwplgtobhgxukwrdlszfaiqxwjvrgxnsveedxseeyeykarqnjrtlaliyudpacctzizcftjlunlgnfwcqqxcqikocqffsjyurzwysfjmswvhbrmshjuzsgpwyubtfbnwajuvrfhlccvfwhxfqthkcwhatktymgxostjlztwdxritygbrbibdgkezvzajizxasjnrcjwzdfvdnwwqeyumkamhzoqhnqjfzwzbixclcxqrtniznemxeahfozp");
160     
161     return 0;
162 }

 

 附上更簡潔的演算法:

1:        int minCut(string s) {  
2:            int len = s.size();  
3:            int D[len+1];  
4:            bool P[len][len];  
5:            //the worst case is cutting by each char  
6:            for(int i = 0; i <= len; i++)   
7:            D[i] = len-i;  
8:            for(int i = 0; i < len; i++)  
9:            for(int j = 0; j < len; j++)  
10:            P[i][j] = false;  
11:            for(int i = len-1; i >= 0; i--){  
12:                 for(int j = i; j < len; j++){  
13:                      if(s[i] == s[j] && (j-i<2 || P[i+1][j-1])){  
14:                           P[i][j] = true;  
15:                           D[i] = min(D[i],D[j+1]+1);  
16:                      }  
17:                 }  
18:            }  
19:            return D[0]-1;  
20:       }  

 

以及使用回溯+剪枝的方法:

1:    int minCut(string s) {  
2:      int min = INT_MAX;  
3:      DFS(s, 0, 0, min);  
4:      return min;  
5:    }  
6:    void DFS(string &s, int start, int depth, int& min)  
7:    {     
8:      if(start == s.size())  
9:      {  
10:        if(min> depth-1)  
11:          min = depth-1;  
12:        return;  
13:      }  
14:      for(int i = s.size()-1; i>=start; i--)  //find the biggest palindrome first
15:      {  
16:        if(isPalindrome(s, start, i))  
17:        {        
18:          DFS(s, i+1, depth+1, min);  
19:        }  
20:       
21:       
22:      }  
23:    }  
24:    bool isPalindrome(string &s, int start, int end)  
25:    {  
26:      while(start< end)  
27:      {  
28:        if(s[start] != s[end])  
29:          return false;  
30:        start++; end--;  
31:      }  
32:      return true;  
33:    }  

 

 

總結下來,要學會分析問題,不能一成不變的只用一個演算法,可能會非常低效。

相關文章