题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=4628
翻译:
给一个字符串,你每次只能删去其中的 回文子串(不要求连续),问最少删多少次能删完
思路:
n不超过16,那首先当然想到状压DP。我是先预处理出所有 回文子串的 所代表的数(cnt个) ,然后把这个问题抽象成 01背包 的问题,有cnt个物品,体力为(1<<n)-1 ,然后再来求就会很方便~详细见代码
注意:处理中灵活运用了 string类 的特性,比如string类重载的 +
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<cmath>
#include<string>
using namespace std;
const int maxn=18;
const int sz=1<<16;
const int mod=1e9+7;
const int inf=2e9+7;
const double pi=3.1415926;
typedef long long LL;
int n,m;
int g[sz];
int dp[sz];
template<class T>
inline void read(T &x)
{
char c; x=1;
while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
T res=c-'0';
while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
x*=res;
}
int main()
{
int t; read(t);
while(t--){
string s;
cin>>s;
int len=s.length();
int num=1<<len,cnt=0;
for(int i=0;i<num;i++){
string t;
for(int j=0;j<len;j++){
if(i&(1<<j)){
t+=s[j]; //string类特性 +重载过了
}
}
int l=t.length();
bool flag=1;
for(int j=1;j<=l/2;j++) if(t[j-1]!=t[l-j]) {flag=0; break;} //判断是否是回文数
if(flag){
g[++cnt]=i;
}
}
memset(dp,127/3,sizeof dp);
dp[0]=0;
for(int i=1;i<=cnt;i++){ //01背包
for(int j=num-1;j>=0;j--){
if(!(g[i]&j)){
dp[g[i]|j]=min(dp[g[i]|j],dp[j]+1);
}
}
}
cout<<dp[num-1]<<endl;
}
return 0;
}