今天做的是合并回文子串
虽说看到题目就知道是个dp,无奈写不出来。
首先来看题意:给两个字符串A和B,现在将A和B进行合并,要求两个字符串中原有的相对位置不发生变化,问合并的可能中最长的回文子串长度?
这里A和B的长度都不超过50.
题目类型:
区间DP
题解已经讲的很好了,这里做个总结:
1.最长回文序列问题
给一个字符串,求一个最长的回文子序列(不要求连续)。
我们用dp[i][j]表示一个字符串从i-j的最长子序列长度。
那么对于dp[i][j]
若a[i]=a[j]则dp[i][j]=dp[i+1][j-1]+2
若a[i]!=a[j]则dp[i][j]=max(dp[i+1][j],dp[i][j-1])
因此我们可以在O(n^2)时间内解决这个问题。#include<bits/stdc++.h> using namespace std; string s,a; int dp[100][100]; int main() { cin>>s; int len=s.size(); for(int i=0;i<len;i++)a[i+1]=s[i]; for(int i=1;i<=len;i++){ dp[i][i]=1; } for(int k=2;k<=len;k++){ for(int i=1;i+k-1<=len;i++){ int j=i+k-1; if(a[i]==a[j])dp[i][j]=dp[i+1][j-1]+2; else dp[i][j]=max(dp[i+1][j],dp[i][j-1]); } } cout<<dp[1][len]<<endl; return 0; }
2.最长回文子串问题
给一个字符串,求一个最长的回文子串(要求连续)
这里我们如果用dp[i][j]表示字符串从i-j的最长子串长度,会发现转移困难的问题,因为子串要求是连续的,如果简单的用dp[i+1][j]或者dp[i][j-1]转移是会出问题的,因为转移过来的不一定能保证是回文子串。
因此我们重新定义dp[i][j]为字符串从i-j能否组成一个回文子串(相当于强制选择i和j两个位置),那么若a[i]=a[j],则dp[i][j]=dp[i+1][j-1]
若a[i]!=a[j],则dp[i][j]=0
这里有一道练习题,https://ac.nowcoder.com/acm/problem/14517
代码如下:#include<bits/stdc++.h> using namespace std; string s,a; int dp[1220][1220]; int main() { while(cin>>s){ memset(dp,0,sizeof(dp)); int len=s.size(); for(int i=0;i<len;i++)a[i+1]=s[i]; for(int k=0;k<=len;k++){ for(int i=1;i+k-1<=len;i++){ int j=i+k-1; if(k<=1){dp[i][j]=1;continue;} //特判1个的时候 if(a[i]==a[j]){ if(k>1)dp[i][j]=dp[i+1][j-1]; } else dp[i][j]=0; } } int maxians=0; for(int i=1;i<=len;i++){ for(int j=i;j<=len;j++){ if(dp[i][j]){ maxians=max(maxians,j-i+1); } } } cout<<maxians<<endl; } return 0; }
再回到这个问题,显然应该是一个最长回文子串问题。注意到长度不超过50。
我们令dp[i][j][k][l]表示A串中i-j部分和B串中k-l部分能否构成回文串。
那么我们可以得到4种情况:
<1>a[i]=a[j]
<2>a[i]=b[l]
<3>b[k]=a[j]
<4>b[k]=b[l]
分别进行讨论就好了,但是要考虑边界问题,这里采用了len1+len2<=1的时候直接令dp为1,直接把边界问题包括其中一个不选等问题解决了,还是很细节的。
#include<bits/stdc++.h> using namespace std; int dp[65][65][65][65]; //表示a[i.j]和b[k.l]能否组成最长回文子串 char a[55],b[55]; int main() { int t; cin>>t; while(t--){ memset(dp,0,sizeof(dp)); scanf("%s",a+1); scanf("%s",b+1); int len1,len2,lena,lenb; lena=strlen(a+1); lenb=strlen(b+1); int maxians=0; for(int len1=0;len1<=lena;len1++){ for(int len2=0;len2<=lenb;len2++){ for(int i=1;i+len1-1<=lena;i++){ int j=i+len1-1; for(int k=1;k+len2-1<=lenb;k++){ int l=k+len2-1; int & tmp=dp[i][j][k][l]; if(len1+len2<=1)dp[i][j][k][l]=1; else { if(len1+len2<=1)tmp=1; else { if(a[i]==a[j]&&len1>1)tmp=max(tmp,dp[i+1][j-1][k][l]); if(a[i]==b[l]&&len1&&len2)tmp=max(tmp,dp[i+1][j][k][l-1]); if(b[k]==a[j]&&len1&&len2)tmp=max(tmp,dp[i][j-1][k+1][l]); if(b[k]==b[l]&&len2>1)tmp=max(tmp,dp[i][j][k+1][l-1]); } } if(tmp)maxians=max(maxians,len1+len2); } } } } cout<<maxians<<endl; } return 0; }