解题思路:
这道题用到了DP,以"gacbbcak"为例进行说明。
首先创建boolean二维数组:
第i行第j列表示字符串第i位到第j位字符是否构成回文串;
例如这个二维数组第3行第5列表示字符串的从第3位到第5位子串,"bbc"是否构成回文串,结果是flase;
左下三角由于i大于j构不成子串所以是空的(比如从原字符串从左到右第5位到第3位是构不成的子串):
填表的顺序为:
填表的原则为:
char[i] == char[j]&&i == j时 构成回文串,为true
例如对于"gacbbcak",i=j=2时,char[i]=char[j]=‘c’,表示的是子字符串"c",是回文的
char[i] == char[j]&&j-i==1时 构成回文串,为true
例如对于"gacbbcak",i=3,j=4时,表示子串"bb",是回文的
char[i] == char[j]&&j-i == 2时 构成回文串,为true
例如对于"aba"字符串来说,当i=0,j=2时,是回文的
char[i] == char[j]&&dp[i+1][j-1]时 构成回文串,为true
例如假设有下面的字符串:
当i=2,j=8时,char[i]==char[j]
同时只要dp[i+1][j-1]即中间那个红框中的部分是回文的,那从第2位到第8位就是回文的。
这里dp[i+1][j-1]是个布尔数值,表示字符串中从i+1位到j-1位是否是回文的
综上,这个二维数组中的元素dp[i][i]为true的条件是:
if(s.charAt(i)==s.charAt(j)&&((j-i<=2)||dp[i+1][j-1]))
dp[i][j]=true;
上面这句代码就包含了上面所有的填表原则。
计算minCut值:
对字符串"gacbbcak"填表:
g | a | c | b | b | c | a | k | |
---|---|---|---|---|---|---|---|---|
g | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
a | 1 | 0 | 0 | 0 | 0 | 0 | 0 | |
c | 1 | 0 | 0 | 1 | 0 | 0 | ||
b | 1 | 1 | 0 | 0 | 0 | |||
b | 1 | 0 | 0 | 0 | ||||
c | 1 | 0 | 0 | |||||
a | 1 | 0 | ||||||
k | 1 |
然后创建一个数组cnt[],用于保存字符串从第i位到最后一位有多少个回文串,i取值0~字符串长度
例如cnt[7]表示从字符串第7位到最后一位,即子串"k"有1个回文串;
cnt[3]表示子串"bbcak"中有多少个子回文串,有4个(“bb”,“c”,“a”,“k”);
计算的原则为:
cnt[i]=Math.min(1+cnt[j+1],cnt[i]);
例如:
在计算cnt[2]时,圆圈1中的dp[2][5]为true,说明方框2中的子串"cbbc"是回文的,那我直接调用cnt[6]即方框4中的数字来看一下剩下的"ak"中有多少个回文串即可。
可以看到cnt[6]=2, 即"ak"中有两个回文串(“a"和"k”)
所以cnt[2]=1+cnt[6]=3,即子串"cbbcak"中有3个回文串(“cbbc”, “a"和"k”)
提交代码:
class Solution{
public int minCut(String s) {
boolean[][] dp=new boolean[s.length()][s.length()];
for(int i=s.length()-1;i>=0;i--)
for(int j=i;j<s.length();j++)
if(s.charAt(i)==s.charAt(j)&&((j-i<=2)||dp[i+1][j-1])) dp[i][j]=true;
int[] cnt=new int[s.length()+1];
for(int i=s.length()-1;i>=0;i--) {
cnt[i]=Integer.MAX_VALUE;
for(int j=i;j<s.length();j++) {
if(dp[i][j])
cnt[i]=Math.min(1+cnt[j+1],cnt[i]);
}
}
return cnt[0]-1;
}
}
运行结果: