Leetcode: Palindrome Partitioning II

参考: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:    }  

 

 

总结下来,要学会分析问题,不能一成不变的只用一个算法,可能会非常低效。

posted @ 2013-08-31 20:15  Avril  阅读(3338)  评论(1编辑  收藏  举报