题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4628
题目大意:
给出一个字符串,每次你可以擦除一个回文子串,问至少需要多少步可以将该字符串全部擦除。
解题思路:
由于字符串的长度最多是16,所以可以采用状态压缩来解这个题目。
dp[i][j]:表示第i步时,状态j是否出现。
源代码:
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
char ch[20];
int dp[20][70000];
string s;
int state[70000];
int cnt;
int ok(string t) //判断字符串是否回文
{
int i,j;
i=0;
j=t.length()-1;
while(i<j)
{
if(t[i]!=t[j]) return 0;
else
{
i++; j--;
}
}
return 1;
}
string zifu(int k) //将状态数转成对应的字符串
{
int len;
string s1;
len=s.length();
s1="";
for(int i=0;i<len;i++)
{
if(k%2==1) s1+=s[i];
k=k/2;
}
return s1;
}
void init(string s) //找出所有的有效状态
{
int k,len,i;
string s1;
len=s.length();
k=(1<<len)-1; //所有状态的总个数
memset(state,0,sizeof(state));
cnt=0;
for(i=1;i<=k;i++)
{
s1=zifu(i);
if(ok(s1))
{
state[cnt++]=i;
}
}
return;
}
bool cmp(int x,int y)
{
return x>y;
}
int main()
{
freopen("in.txt","r",stdin);
int cs,i,d,j,k;
scanf("%d",&cs);
while(cs--)
{
scanf("%s",ch);
s=ch;
i=s.length();
d=(1<<i)-1;
init(s);
sort(state,state+cnt,cmp);
if(state[0]==d)
{
printf("1\n");
continue;
}
memset(dp,0,sizeof(dp));
//枚举第一行出现的有效状态
for(i=0;i<cnt;i++)
{
dp[1][state[i]]=1;
}
int flag=0;
for(i=2;;i++) //枚举接下来的行状态
{
for(j=1;j<=d;j++) //枚举上一行的状态
{
if(dp[i-1][j]==0) continue;
for(k=0;k<cnt;k++) //枚举当前行可以擦除的状态
{
if((state[k]&j)==0) //所取状态与已得状态不冲突
dp[i][state[k]+j]=1;
}
if(dp[i][d]==1)
{
printf("%d\n",i);
flag=1;
break;
}
}
if(flag) break;
}
}
return 0;
}