這道題還挺複雜的,回來看了好一會兒才想起當時怎麼想的。。上道題剛說不要打表,這道題就用了打表。。
總的思路是這樣的,從後面往前面打表,最後一個位置的最小分割一定是0,那往前呢,如果當前考慮的位置是start,並且substr(s, i)是迴文的,那麼如果已知i+1開始的分割次數,那麼start這個位置的分割應該就是start原來的和i+1開始的分割次數加1之間的最小值。DP的思想,很直接。
但是我忽略了一個另一個開銷很大的地方,那就是每次判斷迴文的時候。這個跟單詞分割還不一樣,這個範圍很大。最後居然開了個很大的二維數組,記錄每個位置的起始結束是不是迴文,直接打臉了。。建這個表的時候,我先把兩頭的迴文計算了一遍,當計算中間部分的時候,直接用這兩頭的結果。其實想法很簡單,如果i+1和j-1之間是不是迴文已經知道了,那麼i和j就不用從頭算了,看看i和j位置相不相等,按照情況更新就行了。
這道題應該有更優雅的演算法,回頭找到了貼上來,先貼我的。
int MAX = 0xfffffff;bool isp[2000][2000];bool isPalindrome(string &s, int start, int end){ for(int i=start, j=end;i<j;++i, --j){ if(s[i] != s[j]) return false; } return true;}void getPartition(string &s, int start, vector<int> &minpartition){ if(start == s.length()){ minpartition[start] = 0; return; } for(int i=start;i<s.length();++i){ if(isp[start][i]){ if(minpartition[i+1] == MAX) getPartition(s, i+1, minpartition); minpartition[start] = min(minpartition[start],minpartition[i+1]+1); } }}class Solution {public: int minCut(string s) { if(s.length()<=1) return 0; int len = s.length(); memset(isp, 0, sizeof(isp)); for(int i=0;i<len;i++){isp[0][i] = isPalindrome(s, 0, i);}for(int i=1;i<len;i++){isp[i][len-1] = isPalindrome(s, i, len-1);} for(int i=len-2;i>0;i--){for(int j=i;j<len-1;j++){if(i == j)isp[i][j] = 1;else if(j == i+1)isp[i][j] = s[i]==s[j];elseisp[i][j] = isp[i+1][j-1]&&s[i]==s[j];}} vector<int> minpartition(len+1, MAX); getPartition(s, 0, minpartition); return minpartition[0]-1; }};