132. Palindrome Partitioning II

本文介绍了一种使用动态规划解决最小回文切分问题的方法,通过两次DP过程,首先判断字符串的所有子串是否为回文,然后计算出使得每个子串成为回文所需的最少切割次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.


分析摘自:

http://blog.youkuaiyun.com/doc_sgl/article/details/13418125

http://blog.youkuaiyun.com/yutianzuijin/article/details/16850031

对原分析有修改。


定义函数
D[i,n] = 区间[i,n]之间最小的cut数,n为字符串长度
 a   b   a   b   b   b   a   b   b   a   b   a
                     i                                  n
如果现在求[i,n]之间的最优解?应该是多少?简单看一看,至少有下面一个解
 a   b   a   b   b   b   a   b   b   a   b   a
                    i                    j+1      n
此时  D[i,n] = min(D[i, j] + D[j+1,n])  i<=j <n。

以上是容易想到的,以“acaded”为例,也可以看做下面这张表:


 acaded
a010121
c 01232
a  0121
d   010
e    01
d     0


dp[0][5]=min{dp[0][0]+dp[1][5],dp[0][1]+dp[2][5],dp[0][2]+dp[3][5],dp[0][3]+dp[4][5]}+1

然而这种朴素的DP是O(N^3),华华丽丽TLE,没有利用好回文串的性质。

int[][] dp;
	int[][] isp;
	public int minCut(String s)
	{
		int n=s.length();
		if(n<=1)
			return 0;
		
		dp=new int[n][n];
		isp=new int[n][n];
		
		
		for(int i=0;i<n;i++)
			Arrays.fill(dp[i], -1);
		for(int i=0;i<n;i++)
			dp[i][i]=0;
		
		return dp(0, n-1, s);
	}
	
	private int dp(int x,int y,String s)
	{
		
		if(dp[x][y]>-1)
			return dp[x][y];
		if(ispali(s.substring(x,y+1)))
			return dp[x][y]=0;
		int minxy=Math.min(x, y);
		int min=Integer.MAX_VALUE;
		for(int j=minxy,i=x+1;j<y;i++,j++)
		{
			int sum=dp(x, j, s)+dp(i, y, s)+1;
			if(sum<min)
				min=sum;
		}
		
		return dp[x][y]=min;	
		
	}
	
	private boolean ispali(String s)
	{
		int strlen=s.length();
		for(int i=0,j=strlen-1;i<strlen/2;i++,j--)
			if(s.charAt(i)!=s.charAt(j))
				return false;
		return true;
	}

------------------------------------------------------------------------------------------------------

换成一维DP。如果每次,从i往右扫描,每找到一个回文就算一次DP的话,就可以转换为
D[i] = 区间[i,n]之间最小的cut数,n为字符串长度, 则,
D[i] = min(1+D[j+1] )    i<=j <n    【这里不再随便切割,而是选到前面满足回文才切割】
有个转移函数之后,一个问题出现了,就是如何判断[i,j]是否是回文?每次都从i到j比较一遍?太浪费了,这里也是一个DP问题。
定义函数
P[i][j] = true if [i,j]为回文
那么
P[i][j] = ((str[i] == str[j]) && (P[i+1][j-1]));


公式的含义是,从i开始的字符串的最小划分为:如果从位置i到位置j的子串是回文串,则从i开始的划分可以通过将i到j的子串看作划分的一部分,然后加上从j+1位置开始的子串最小划分,并选择可能情况中的最小值即为从i开始的最小划分。

完成通项公式的构造,下一步是考虑计算顺序和初始化。由于count[i]依赖于比i更大的count元素,所以i循环应该倒序。j的取值范围从i到len-1,正序和倒序均可。需要初始化的元素为最后一个count元素,其满足count[len]=0。

public class Solution {
    public int minCut(String s) {
        int[][] dp=new int[s.length()][s.length()];
        int[] count=new int[s.length()+1];
        
        for(int i=s.length()-1;i>=0;i--)
        {
            count[i]=Integer.MAX_VALUE;
            for(int j=i;j<s.length();j++)
            {
                if(s.charAt(i)==s.charAt(j)&&(j-i<2||dp[i+1][j-1]==1))
                {
                	dp[i][j]=1;
                	count[i]=Math.min(1+count[j+1],count[i]);
                }
            }
        }
        
        return count[0]-1;       
    }
}



update 2016.07.22

第一次DP先判断所有[i,j]是否回文串

第二次DP,count[i] =min{ count[j-1] +1} 当 [j,i]是回文串  1<=j<=i 

public int minCut(String s) 
	{
		int len=s.length();  
	    boolean[][] dp=new boolean[len][len];  
	    for(int i=len-1;i>=0;i--)  
	    	for(int j=0;j<len;j++)  
		      {  
		         if(i>=j)  
		           dp[i][j]=true;  
		         else {  
		               dp[i][j]=dp[i+1][j-1]&&(s.charAt(i)==s.charAt(j));  
		              }  
		      }  
	       
	    int[] count=new int[len+1];
	    
	    Arrays.fill(count, Integer.MAX_VALUE);
	    count[0]=0;
	    
	    for(int i=1;i<=len;i++)
		    for(int k=1;k<=i;k++)
		    	if(dp[k-1][i-1]==true)
		    		count[i]=Math.min(count[i], count[k-1]+1);
	    
	    return count[len]-1;
	    
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值