zoukankan      html  css  js  c++  java
  • 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:    }  

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

  • 相关阅读:
    vs2008打开aspx文件时设计界面死机情况的解决
    数据库设计知识点
    JS从样式表取值的函数currentStyle(IE),defaultView(FF)
    Iframe选区
    实用正则表达式(实用篇)
    46.class属性 Walker
    410.锚链接和空链接 Walker
    45.ID属性 Walker
    49.文件下载 Walker
    47.title和style属性 Walker
  • 原文地址:https://www.cnblogs.com/avril/p/3293449.html
Copyright © 2011-2022 走看看