Tuesday, September 10, 2013

Palindrome Partitioning II

Q: 
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.

A:
Dynamic programming. (quite similar to Matrix - chain problem)
public class PalindromePartitioningII {
 public int minCut(String s) {
  int n = s.length();
  if (n == 0)
   return 0;
  // use an matrix to record if a subString of s is palindrome.
  boolean[][] isPalindromeArray = new boolean[n][n]; // record only i<=j
  buildPalindromeArray(s, isPalindromeArray);

  // for each just added s[i], record which minimum cut for s[0....i],
  // and its number
  int[] minCutNum = new int[n + 1]; // minCutNum[i] means the after the
           // ith position
  minCutNum[0] = -1;

  // add each char from the front to end
  for (int j = 1; j <= n; j++) {
   minCutNum[j] = minCutNum[j - 1] + 1;
   // now we combine the suffix part, with s.charAt(i)
   for (int i = j - 1; i >= 1; i--) {
    if (isPalindromeArray[i - 1][j - 1]) {// if substring s[i-1, ...
              // ,j-1] is palindrome
     minCutNum[j] = Math.min(minCutNum[j], 1 + minCutNum[i - 1]);
    }
   }
  }
  return minCutNum[n];
 }

 private void buildPalindromeArray(String s, boolean[][] isPalindromeArray) {
  // following matrixly calling method is too time consuming, because of
  // the repeated calling the function
  // Solution : using the dynamic programming philosophy.
  // using the small edge to build the long edge
  // for (int i = 0; i < n; i++) {
  // for (int j = i + 1; j < n; j++) {
  // isPalindromeArray[i][j] = isPalindrome(s.substring(i, j + 1));
  // }
  // }
  for (boolean[] row : isPalindromeArray)
   Arrays.fill(row, true); // initialize the row as TRUE,
  // for substring length of i=j, means one character, it is already set
  // to zeros
  for (int len = 1; len < s.length(); len++) { // here , length = j - i
   int j; // j is the end point of a substring
   for (int i = 0; i < s.length() - len; i++) {
    j = i + len;
    isPalindromeArray[i][j] = (s.charAt(i) == s.charAt(j) && isPalindromeArray[i + 1][j - 1]);
   }
  }
 }
}


上面这个想法,还是挺SB的,毕竟要n^2的空间。   到底是自己做的第二道leetcode 的题目啊。

新想法如下,有一个链表数组: 对每个位置, 有该位置可以与previous position  构成 palindrome 的位置的信息
另外加一个minCut[] 来记录对每一个地址,其number of minimum cut.

public class Solution {
    public int minCut(String s) {
        int n = s.length();
        int[] minCut = new int[ n ];
        List<List<Integer>> paliPos = new ArrayList<>();
        for(int i =0;i<n;i++){
            List<Integer> al = new ArrayList<>();
            al.add(i);
            paliPos.add(al);
        }
        for(int i=1;i<n;i++){
            char ch = s.charAt(i);
            minCut[i] = 1 + minCut[i-1];
            if(ch == s.charAt(i-1) ){ // direct match with the left
                minCut[i] = Math.min(minCut[i],  (i==1) ? 0: 1+minCut[i-2] );
                paliPos.get(i).add(i-1);
            }
            List<Integer> leftPosList = paliPos.get(i-1);
            for(int pos : leftPosList){
                if(pos>0 && s.charAt(pos-1) == ch){
                    paliPos.get(i).add(pos-1);
                    minCut[i] = Math.min( minCut[i], pos ==1 ? 0 : 1 + minCut[pos-2] );
                }
            }
        }
        return minCut[n-1];
    }
}


-----------------------------思路1 的重新实现  (第二遍做的)--------------------------------------
public class Solution {
     public int minCut(String s) {
        if(s.length()<=1 )
            return 0;
        // build a 2D array, indicating whether each [i,j] position makes an palindrome
        int n = s.length();
        boolean[][] isPalin = new boolean[n][n];
        //build the whole set
             for(int j =0;j<n;j++){
                isPalin[j][j] = true;  //set [i][i] == true
                for(int i = j-1; i>=0; i--){
                    isPalin[i][j] = s.charAt(i)==s.charAt(j) 
     &&( isPalin[i+1][j-1] || i+1 == j);
                }
            }
        // build a minCut from begining to end
        int[] minCut = new int[n];
        for(int i =1;i<n;i++){
            minCut[i] = minCut[i-1] +1;
            //check all its previous mathing
            for(int j = 0;j<i;j++){
                if(isPalin[j][i] == true){
                    if(j ==0){
                        minCut[i] = 0;
                    }else{ // coz we gonna use j-1 as index
                        minCut[i] = Math.min(minCut[j-1] +1,minCut[i]);
                    }
                }
            }
        }
         return minCut[n-1];
     }
}






(呵呵,看来,做题还是有进步的。╮(╯▽╰)╭    刚开始,光晕去了)

 Mistakes:
1: when input string is too large, still declaring the isPalindromeArray[n][n] is too time comsuming  
 2: when building MatchPos ArrayList, I forget the case that the char at i_th position could be matched directly with one single ahead.
例如: aab,  得到的list 应该是[0][0,1][0,1,2]
 因此要测试, i 和i-1的匹配( 在  如果两个紧挨着, 就无法分出preMatch来了

3: 2遍: 在建立2D isPalindrome array的时候,应该从 没加入一个字符, 基于 其前面所有的match 的情况已经计算出来。
因此, for (j =0, j< n;j++)
                   for ( i =j-1;i >=0;i--)
4:  2遍;  在基于之前的计算的时候, 要考虑,前面的是否存在,  亦即 i ==j-1的时候。

Learned: 
TPTP 等分析代码运行时间的插件都不能用。  还是用最简单的 打印出各自运行的时间就行。
 
long beginTime = System.nanoTime();

        System.out.println((System.nanoTime() - beginTime)/10000);

        beginTime = System.nanoTime();
然后,每次upddate beginTime, 对比就行。 System.currentTimeMillis vs System.nanoTime  都是返回long类型,但是区别呢???

2: Arrays.fill() 函数,只能对一维数组填充。 多维的,要自己去一维得填 (可以利用Arrays.fill() 函数)

2: 如果是在eclipse中运行,需要import 一些库,例如import java.util.*;


别人的解法:(但基本上,还是自己的最好,不用判断回文的函数。)
有一个是利用DFS回溯的。调用O(n^2) 次dfs, 貌似不太好。

-----------------------------利用递归调用,每次取后面的----------

No comments:

Post a Comment