[LintCode] Palindrome Partitioning II

Given a string s, cut s into some substrings such that every substring is a palindrome.

Return the minimum cuts needed for a palindrome partitioning of s.

Example

Given s = "aab",

Return 1 since the palindrome partitioning ["aa", "b"] could be produced using 1 cut.

 

Solution 1. Recursion and backtracking

A straightforward solution is to check all valid partitions and get the minimum cut.

1. First fix a substring and check if palindromic, if it is, add this substring to a list and 

recursively partition the rest of the string.

2. After the entire string is partitioned, update the min cut if necessary.

3. Backtrack to check other valid partitions. 

 

Efficiency: This algorithm is very inefficient for the following two reasons.

1. The same subproblem is computed redundantly. 

  The following recursion tree shows this.

            "aabbc"

     "abbc"              "bbc"            

   "bbc" 

 2.  Substring palindrome check does not use smaller substring's check result.

       For string "abcd",  if we've checked "bc" is not palindromic, then for sure 

  "abcd" is not palindromic. isPalindorme() does the O(n) check for all substrings,

  not using previous check results. 

 1 public class Solution {
 2     public int minCut(String s) {
 3         if(s == null || s.length() == 0)
 4         {
 5             return -1;
 6         }
 7         ArrayList<Integer> minCut = new ArrayList<Integer>(1);
 8         minCut.add(s.length() - 1);
 9         
10         minCutHelper(minCut, new ArrayList<String>(), s, 0);
11 
12         return minCut.get(0);
13     }
14     
15     private void minCutHelper(ArrayList<Integer> currMinCut, 
16                               List<String> curr,
17                               String s, 
18                               int startIdx)
19     {
20         if(curr.size() > 0 && startIdx >= s.length())
21         {
22             if((curr.size() - 1) < currMinCut.get(0))
23             {
24                 currMinCut.set(0, curr.size() - 1);
25             }
26             return;
27         }
28         
29         for(int i = startIdx; i < s.length(); i++)
30         {
31             if(isPalindrome(s, startIdx, i))
32             {
33                 curr.add(s.substring(startIdx, i + 1));
34                 minCutHelper(currMinCut, curr, s, i + 1);
35                 curr.remove(curr.size() - 1);
36             }
37         }
38     }
39     
40     private boolean isPalindrome(String str, int left, int right)
41     {
42         while(left <= right)
43         {
44             if(str.charAt(left) != str.charAt(right))
45             {
46                 return false;
47             }
48             left++;
49             right--;
50         }
51         return true;
52     }
53 }

 

Solution 2. Dynamic Programming

To address the two performance issues in solution 1, we make some time-space tradeoff.

State:

pal[i][j] stores if s[i....j] is palindromic or not. 

minCuts[i] stores the minimal cuts needed to partition substring s[0....i].

Initialization:

pa[i][i] = true, as one character is palindromic.

pa[i][i + 1] = s.charAt(i) == s.charAt(i + 1).

minCuts[i] = i, as the most cuts for a string of length i + 1 is i. 

Function:

pal[i][j] = pal[i + 1][j - 1] && s.charAt(i) == s.charAt(j);  compute the result of shorter length substrings first, as they will be used to compute longer substrings' result.

For a given substring s[0...j], we check all possible partitions.

If pal[i][j] == true, s[i....j] is a valid palindromic partition, then the problem of s[0...j] can be reduced to s[0... i - 1] + 1, 1 represents the partition of s[i][j].

  a.  if i == 0, if s[0...j] is palindromic, then no cut is needed for s[0...j].

        minCuts[j] = 0;

  b.  if i > 0, 

     minCuts[j] = Math.min(minCuts[j], minCuts[i - 1] + 1), i is from 1 to j;

 

 1 public class Solution {
 2     public int minCut(String s) {
 3         if(s == null || s.length() <= 1){
 4             return 0;
 5         }    
 6         int len = s.length();
 7         int[] minCuts = new int[len];
 8         boolean[][] pal = new boolean[len][len];
 9         for (int i = 0; i < len; i++) {
10             pal[i][i] = true;
11         }
12         for (int i = 0; i < len - 1; i++) {
13             pal[i][i + 1] = (s.charAt(i) == s.charAt(i + 1));
14         }
15         for(int i = 0; i < len; i++){
16             minCuts[i] = i;
17         }
18         for (int i = len - 3; i >= 0; i--) {
19             for (int j = i + 2; j < len; j++) {
20                 pal[i][j] = pal[i + 1][j - 1] && s.charAt(i) == s.charAt(j);
21             }
22         }
23         for(int i = 0; i < len; i++){
24             for(int j = 0; j <= i; j++){
25                 if(pal[j][i]){
26                     if(j == 0){
27                         minCuts[i] = 0;
28                     }
29                     else{
30                         minCuts[i] = Math.min(minCuts[i], minCuts[j - 1] + 1);
31                     }
32                 }
33             }
34         }
35         return minCuts[len - 1];
36     }
37 }

 

The above dp uses a 1D array minCuts[i] to store min cut of substring s[0....i].  

This is similar with Text Justification and Word Break.

Text Justification:  minCost[i] = min { minCost[j] + cost[j + 1][i] }

Word Break:  dp[i] = dp[i - lastWordLen] && dict.contains(s.substring(i - lastWordLen, i));  

      dp[i]: if s[0....... i - 1] can be broken into words in dict.

 

Related Problems 

Wiggle Sort II

Palindrome Partitioning 

Longest Palindromic Substring

转载于:https://www.cnblogs.com/lz87/p/7432883.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值