Labels

Tuesday, February 3, 2015

Palindrome Partitioning II


Palindrome Partitioning II




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.

(注:写的废话有点多,可以直接看我在leetcode discuss的提问https://oj.leetcode.com/discuss/24253/how-to-get-from-o-n-3-to-o-n-2-java-solution-sharing
Naive Way: 好像有上一题的DP的二维矩阵就可以很快得到最小割了。

基本一致的回溯方式,这样的算法复杂度是O(2^n),最坏的情况。然后会出现超时。

public class Solution {
    public int minCut(String s) {
        boolean opt[][] = new boolean[s.length()][s.length()+1];
        // base case
        for(int i = 0;i < s.length();i++){
            opt[i][i+1] = true;
            opt[i][i] = true;
        }
        // iteration
        for(int i = 2;i <= s.length();i++)
            for(int j = 0;j+i <= s.length();j++)
                opt[j][j+i] = s.charAt(j)==s.charAt(j+i-1) && opt[j+1][j+i-1];
        
        return search(0,opt,-1);
    }
    
    private int search(int begin, boolean[][] opt, int cut){
        int min = opt[0].length;
        if(begin==opt[0].length-1)
            return cut;
        for(int i = begin+1;i < opt[0].length;i++)
            if(opt[begin][i])
                min = Math.min(search(i,opt,cut+1),min);
        return min;
    }
}


Improved Way:这时我想到O(n)求longest palindrome substring的方法。因为那个方法能在O(n)的时间内求出所有字符的覆盖范围,那么用贪心的思想取覆盖范围大的,就可以实现最小分割数了。

实际做时,发现这种greedy的思想会产生一个bug。当String = aaaba的时候。
0  1  2  3  4  5  6  7  8  9  10
#  a  #  a   #  a  #  b  #  a  #
1  2  3  4  3  2  1  4  1  2  1

先取第一个4就会得到{"aaa","b","a"}
先取第二个4就会得到{"aa","aba"}
一个是3割一个是2割。
这个BUG只会在OJ的倒数第二种测例中出现,很容易忽略。同时,这个例子也可以说明对DP的结果进行遍历时,不能够用greedy的思想去最大长度的找一个max(j-i) 其中 opt[i][j]=true。否则就会出现这里这种先找前3个a而导致后面必须用多一个割的情况。



这时看了一眼这道题的标签,是DP耶。幡然醒悟,应该用一个更直接的DP。
基本逻辑为:
opt(i,j) //表示将String s[i-j]分成全palindrome子串的最少割数。
// base case
opt(i,i) = 0     0<= i < s.length()
// iteration
opt(i,j) =
if s[i]==s[j] && opt(i+1,j-1)==0
     0
else
     min(opt(i,t)+opt(t+1,j)+1)  for  i<= t < j

算法复杂度为O(n^3),但还是超时了。
public class Solution {
    public int minCut(String s) {
        // DP
        int opt[][] = new int[s.length()][s.length()];
        // base case 0
        // iteration
        for(int i = 1;i < s.length();i++){
            for(int j = 0;j+i < s.length();j++){
                if(s.charAt(j)==s.charAt(j+i) && opt[j+1][j+i-1]==0){
                    opt[j][j+i] = 0;
                }else{
                    opt[j][j+i] = i;
                    for(int t = j;t < j+i;t++)
                        opt[j][j+i] = Math.min(opt[j][j+i], opt[j][t]+opt[t+1][j+i]+1);
                }
            }
        }
        return opt[0][s.length()-1];
    }
}


可以不可以在O(n^2)内完成呢?如果DP只有一个参数呢?
opt[i] // 表示分割s[0-i]所需最小分割数。
 // base case
opt[0] = 0;
// iteration
opt[i] =
if(s[0-i] is palindrome)
     0
else
    min(opt[t] + s[t+1 ~ i] is palindrome?1:i-t)

这样做虽然只有O(n)的外层循环,但是每一次循环都要遍历之前所有还要同时判断当前的是否为palindrome,每一次循环就需要O(n^2),所以还是O(n^3)。

还是看了一下discuss发现自己好愚蠢,在一开始就已经在O(n^2)的时间内求出所有s[i~j]是否为palindrome了。所以每一层循环现在只需要O(n)的时间,总的时间就是O(n^2)了。


public class Solution {
    public int minCut(String s) {
        // use DP to determine any palindrome substring
        boolean opt[][] = new boolean[s.length()][s.length()+1];
        // base case
        for(int i = 0;i < s.length();i++){
            opt[i][i+1] = true;
            opt[i][i] = true;
        }
        // iteration
        for(int i = 2;i <= s.length();i++)
            for(int j = 0;j+i <= s.length();j++)
                opt[j][j+i] = s.charAt(j)==s.charAt(j+i-1) && opt[j+1][j+i-1]; 
        
        
        // use DP to determine min cut
        int cut[] = new int[s.length()+1];
        for(int i = 1;i <= s.length();i++){
            if(opt[0][i])
                cut[i] = 0;
            else{
                cut[i] = i-1;
                for(int t = 1;t < i;t++){
                    cut[i] = Math.min(cut[i],cut[t] + (opt[t][i]?1:i-t));
                }
            }
        }
        return cut[s.length()];
        
    }
}

 

No comments:

Post a Comment