题目:要求将一个序列划分为最少个回文子串。
如果不加思索,想当然将局部问题F[i][j]定义为:从ai到aj的子串可以划分的回文串最少个数;那么求F[i][j]需要枚举子区间,时间复杂度为O(n^3);
F[i]定义为:从ai到序列尾端的子串可以划分为的回文串的最少个数;
我们做完预处理:
a[i][j]为1即ai到aj的序列是回文子串;a[i][j]=0,即表示不能形成回文子串;
O(n^2)就能完成!
我们不需要再求那些从ai到aj的子串(aj不为序列末尾)可以划分的回文串最少个数,一样需要枚举左半部分,只是只有左半部为回文串才讨论划分。其它不为回文串的可能情况都会包含在仅划掉ai的情况里(ai本身形成回文串);
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <string.h>
#include <math.h>
using namespace std;
int F[1100][1100];
int g[1100];
char a[1100];
int dfs(int t,int len){
if(t>len)return 0;
int &k=g[t];
if(k!=-1)return k;
k=1e9;
for(int i=t;i<=len;++i)
{
if(F[t][i]) k=min(k,dfs(i+1,len)+1);
}
return k;
}
int main(){
int T;scanf("%d",&T);
int kase=0;
while(T--){
memset(g,-1,sizeof(g));
scanf("%s",a);
int len=strlen(a);
for(int i=1;i<=len;++i)
{
F[i][i]=1;
F[i][i-1]=1;
}
for(int l=2;l<=len;++l){
for(int i=1;i+l-1<=len;++i){
int last=i+l-1;
if(a[i-1]==a[last-1]&&F[i+1][last-1]==1)F[i][last]=1;
else F[i][last]=0;
}
}
printf("Case %d: %d\n",++kase,dfs(1,len));
}
return 0;
}