Description
在2.14(情人节)的午夜,kkun作为一只程序猿并不难过,并且向单身狗julyc发动了嘲讽技能:
julyc非常伤心,因为作为一只咸鱼直到年前都没有做完boss的任务,所以也没有时间去找学妹加强交流,因此他决定向各位请求帮助,而对于帮助julyc解决问题的小伙伴julyc就会偷偷告诉他们学姐cfenglv的qq啦。。
回文串划分就是将一个字符串划分为若干字串,并且每个字串都是一个回文串。
例如,字符串“ABACABA”可以有几种不同的划分方式,例如{"A","B","A","C","A","B","A"}, {"A","BACAB","A"}, {"ABA","C","ABA"}, or {"ABACABA"}。
julyc的任务是将给定字符串,确定其得到最少子串数量的回文串划分方式的子串数量。
Input
输入第一行包含一个整数T(T<=40),代表测试数据组数。每组测试数据中包含一个只包含大写字母的非空字符串s(length(s)<=1000)
Output
对于每组测试数据,你们应该输出测试数据组数跟答案。每组输出占一行。
Sample Input 1
4 AAAA ABCDEFGH QWERTYTREWQWERT ABACCC
Sample Output 1
Case 1: 1 Case 2: 8 Case 3: 5 Case 4: 2
Hint
对于第三个样例,最少子串数的分割方案为:“QWERTYTREWQ”,“W”,“E”,“R”,“T”,分成了五个回文串
对于第四个样例,最少子串数的分割方案为:“ABA”,“CCC”,分成了两个回文串
#include<cstdio>
#include<cstring>
#include<algorithm>
#define bug(x) printf("%d***\n",x)
typedef long long ll;
using namespace std;
const int maxn=1010;
char str[maxn];
int dp[maxn][maxn];
int f[maxn];
/*
错了两个地方, 第一个是走的方向不对,我们没有实现已知的状态的获取
第二个就是我们不知道这个字符串是不是回文串,事实就是只要dp[i][j]=1,的话
就说明它是回文串
*/
int main(){
int n;
scanf("%d",&n);
int T=1;
while(n--){
scanf(" %s",str);
int len=strlen(str);
memset(dp,0,sizeof(dp));
for(int j=0;j<len;j++){
for(int i=j;i>=0;i--){
if(i==j){
dp[i][j]=1;continue;
}
if(i+1==j){
dp[i][j]=str[i]==str[j]?1:2;continue;
}
if(dp[i+1][j-1]==1&&str[i]==str[j]){
dp[i][j]=1;continue;
}
dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1;
/*for的缺点 超时 溢出 ,但是思路简单清晰
dp[i][j]=dp[i+1][j]+1;
for(int k=i+1;k<j;k++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
dp[i][j]=min(dp[i][j],dp[i][j-1]+1);
for(int k=j-1;k>i;k--)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
*/
}
}
/*
这个循环真的是太牛了,把我们的每次精确的更新,都放到最后
换一句话说,我们只需要判断哪些是回文的字符串,然后再最后枚举就行了
如果在过程中每次都更新的话,我们指定超时
*/
for(int j=0;j<len;j++){
if(j==0) f[j]=1;
else f[j]=f[j-1]+1;
for(int i=j-1;i>=0;i--)
if(dp[i][j]==1)
f[j]=min(f[j],f[i-1]+1);
}
printf("Case %d: %d\n",T++,f[len-1]);
}
return 0;
}