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.
Example:
Input: "aab" Output: 1 Explanation: The palindrome partitioning ["aa","b"] could be produced using 1 cut.
寻找分割回文串需要的最小切数,算是“验证回文串” “分割回文串”的衍生题。
一开始我用动态规划做,dp[i]代表在字符串从0到i需要的最小切书。
递推公式为dp[i+1] = min(dp[k]+1)
where: 0<=k<i and s.substring(k,i+1) is a palindrome。
显然这种做法很低效,如果判断回文串的时间是O(n)的话,这样的动态规划需要O(n^3)。虽然侥幸通过了,但是执行用时1558ms,排在后5%,非常丢人。
学习了一下前5%的代码,他们同样使用的是dp,但是思路有所不同:
同样遍历字符串,当遍历到i时,考虑若从i向左向右展开出的回文串不被切开,且该回文串的开始为s结尾为k,那么1+dp[s-1]是一种切法。将其与dp[k]进行比较,取较小值进行更新。遍历完字符串后也就更新完成了所有值。
当然这种方法的正确性不是特别直观:当遍历到i时,只可能会更新i以及i之后的最小切数。这意味这需要证明当遍历到i时,i之前已经更新完成的最小切数一定是正确的。
动态规划的证明,感觉用数学归纳法就非常容易。具体证明细节我就不写了。
最后放上我自己的后5%的低效代码:
class Solution {
public int minCut(String s) {
int[] dp = new int[s.length()];
for(int i = 1;i<s.length();i++)
{
dp[i] = dp[i-1]+1;
for(int k = 0;k<=i;k++)
{
if(isPalindrome(s.substring(k,i+1)))
{
if(k==0)
dp[i] = 0;
else
{
if(1+dp[k-1]<dp[i])
dp[i] = 1+dp[k-1];
}
}
}
}
return dp[s.length()-1];
}
public boolean isPalindrome(String s) {
int low = 0, high = s.length()-1;
boolean flagL = false, flagR = false;
char tempL=' ', tempR='#';
while(low<high)
{
if(!flagL)
if(Character.isUpperCase(s.charAt(low)))
{
tempL = (char)(s.charAt(low)+32);
flagL = true;
}
else if(Character.isLowerCase(s.charAt(low)) || (s.charAt(low)<='9' && s.charAt(low)>='0'))
{
tempL = s.charAt(low);
flagL = true;
}
else
low++;
if(!flagR)
if(Character.isUpperCase(s.charAt(high)))
{
tempR = (char)(s.charAt(high)+32);
flagR = true;
}
else if(Character.isLowerCase(s.charAt(high)) || (s.charAt(high)<='9' && s.charAt(high)>='0'))
{
tempR = s.charAt(high);
flagR = true;
}
else
high--;
if(flagL && flagR)
{
if(tempL!=tempR)
return false;
low++;
high--;
flagL=false;
flagR=false;
}
}
return true;
}
}