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 }