LightOJ1044 Palindrome Partitioning(区间DP+线性DP)

问题问的是最少可以把一个字符串分成几段,使每段都是回文串。

一开始想直接区间DP,dp[i][j]表示子串[i,j]的答案,不过字符串长度1000,100W个状态,一个状态从多个状态转移来的,转移的时候要枚举,这样时间复杂度是不可行的。

然后我就想降维度了,只能线性DP,dp[i]表示子串[0,i]的答案。这样可以从i-1转移到i,str[i]单独作一段或者str[i]能和前面的组成回文串,方程如下:

dp[i]=min(dp[i-1]+1,dp[j-1]+1) (子串[j,i]是回文串)

 

现在问题是怎么快速判断一个字符串的任意子串是否是回文串。

我想该不会要用字符串的一些数据结构或算法吧。。忽然又想到区间DP,这个问题是可以用区间DP解决的:

dp2[i][j]表示子串[i,j]是否是回文串

而转移只要一步即可:

dp2[i][j] = (str[i]==str[j] && dp2[i+1][j-1])

因此这就可以在O(strlen2)预处理完并在O(1)时间复杂度下判断任意区间是否是回文串。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 char str[1111];
 6 bool palindrome[1111][1111];
 7 int d[1111];
 8 int main(){
 9     int t;
10     scanf("%d",&t);
11     for(int cse=1; cse<=t; ++cse){
12         scanf("%s",str);
13         int n=strlen(str);
14 
15         for(int i=0; i<n; ++i){
16             for(int j=0; j<n; ++j){
17                 palindrome[i][j]=(i>=j);
18             }
19         }
20         for(int len=2; len<=n; ++len){
21             for(int i=0; i+len<=n; ++i){
22                 if(str[i]==str[i+len-1] && palindrome[i+1][i+len-2]) palindrome[i][i+len-1]=1;
23             }
24         }
25 
26         d[0]=1;
27         for(int i=1; i<n; ++i){
28             if(palindrome[0][i]){
29                 d[i]=1;
30                 continue;
31             }
32             d[i]=d[i-1]+1;
33             for(int j=i-1; j>=1; --j){
34                 if(palindrome[j][i]) d[i]=min(d[i],d[j-1]+1);
35             }
36         }
37         printf("Case %d: %d\n",cse,d[n-1]);
38     }
39     return 0;
40 }

 

posted @ 2016-01-14 17:32  WABoss  阅读(201)  评论(0编辑  收藏  举报