合并回文子串
题目描述 :
输入两个字符串A和B,合并成一个串C,属于A和B的字符在C中顺序保持不变。如"abc"和"xyz"可以被组合成"axbycz"或"abxcyz"等。
我们定义字符串的价值为其最长回文子串的长度(回文串表示从正反两边看完全一致的字符串,如"aba"和"xyyx")。
需要求出所有可能的C中价值最大的字符串,输出这个最大价值即可。
输入描述:
第一行一个整数T(T ≤ 50)。
接下来2T行,每两行两个字符串分别代表A,B(|A|,|B| ≤ 50),A,B的字符集为全体小写字母。
思路: 首先类比求单独一个字符串的最长回文子串的区间DP的方法,即二维DP维护即可
状态转移方程:
if(s[i-1]==s[j-1]) dp[i][j] = dp[i+1][j-1]
else dp[i][j] = 0
(以小区间拓展到大区间)
此题由两个字符串保持原序列的条件下,组成一个最长的回文子串,所以我们用四维DP,且同样的用0,1表示状态,那么除去边界情况,每次状态转移就只有四种选择 (可以手动画一下区间)。
所以本题状态转移方程 :
if(s1[i-1]==s1[j-1]) dp[i][j][k][l] |=dp[i+1][j-1][k][l];
if(s1[i-1]==s2[l-1]) dp[i][j][k][l] |=dp[i+1][j][k][l-1];
if(s2[k-1]==s2[l-1]) dp[i][j][k][l] |=dp[i][j][k+1][l-1];
if(s2[k-1]==s1[j-1]) dp[i][j][k][l] |=dp[i][j-1][k+1][l];
dp[i][j][k][l] 表示 s1 字符串的 i - j 区间和 s2 字符串的 k - l 区间是否能够构成回文数组。
Code:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 55;
int dp[N][N][N][N];
int main()
{
int T;
cin>>T;
while(T--)
{
string s1,s2;
cin>>s1>>s2;
int len1 = s1.size();
int len2 = s2.size();
int ans = 0;
for(int x = 0;x <= len1; x++) //枚举长度
for(int y = 0;y <= len2; y++) //枚举长度
for(int i=1,j=x;j<=len1;i++,j++) //枚举区间
for(int k=1,l=y;l<=len2;k++,l++){ //枚举区间
if(x+y<=1) dp[i][j][k][l] = 1; //单独一个字母是回文
else{
dp[i][j][k][l] = 0; //多组读入 初始化
if(x>1 && s1[i-1]==s1[j-1] && dp[i+1][j-1][k][l])
dp[i][j][k][l] = 1;
if(y>1 && s2[k-1]==s2[l-1] && dp[i][j][k+1][l-1])
dp[i][j][k][l] = 1;
if(x &&y && s1[i-1]==s2[l-1] && dp[i+1][j][k][l-1])
dp[i][j][k][l] = 1;
if(x &&y && s2[k-1]==s1[j-1] && dp[i][j-1][k+1][l]) //四种状态转移
dp[i][j][k][l] = 1;
}
if(dp[i][j][k][l]) ans= max(ans,x+y);
// 意味着区间i~j 和k~l 能构成回文,则长度为(j-i+1 + k-l+1) 即 (x+y)
}
cout<<ans<<endl;
}
return 0;
}