题目大意:见刘汝佳《算法竞赛入门经典(第2版)》P275
解题思路:
有点类似最长上升子序列的一个DP问题。
设 dp[i] 为对于字符串 s[1,...,i] 的最少回文串数。转移方程为:dp[i] = min{dp[j] + 1 | j<i,s[j+1,...,i]为回文串}。那么问题就是如何判断某个字符串子串是不是回文串。本来DP这部分的时间复杂度就是O(n^2),如果再对于每一个“状态”加上一个O(n)的循环来判断是否为回文数的话,时间复杂度就变成 O(n^3)带入数据范围一算不难发现会超时,那么我们只能预处理判断的这部分。请看程序及注释。
AC代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 6 using namespace std; 7 const int maxn=1000+3,inf=0x7fffffff; 8 char inp[maxn]; 9 bool is_pp[maxn][maxn]; 10 int dp[maxn]; 11 int main() 12 { 13 int n; 14 scanf("%d",&n); 15 while(n--){ 16 memset(is_pp,false,sizeof(is_pp)); 17 scanf("%s",inp+1); 18 int len=strlen(inp+1); 19 //************************************************************************** 20 //预处理部分 21 for(int i=1;i<=len;i++){ //枚举中点 22 is_pp[i][i]=true; //单个字符肯定是回文串 23 for(int j=1;;j++){ //向左右伸展 24 if(i-j<1||i+j>len) break; 25 if(inp[i-j]==inp[i+j]) is_pp[i-j][i+j]=true; 26 else break; //一旦发现左右不等,当即退出 27 } 28 if(i+1<=len&&inp[i]==inp[i+1]){//上面一个循环处理的是字符串长度为奇数的部分,这个循环处理的是字符串长度为偶数的部分 29 is_pp[i][i+1]=true; 30 for(int j=1;;j++){ 31 if(i-j<1||i+j+1>len) break; 32 if(inp[i-j]==inp[i+j+1]) is_pp[i-j][i+j+1]=true; 33 else break; 34 } 35 } 36 } 37 //************************************************************************* 38 dp[0]=0; 39 for(int i=1;i<=len;i++){ 40 dp[i]=inf; 41 for(int j=0;j<i;j++){ 42 if(is_pp[j+1][i]) 43 dp[i]=min(dp[i],dp[j]+1); 44 } 45 } 46 printf("%d ",dp[len]); 47 } 48 return 0; 49 }