合并回文子串 (区间DP)

合并回文子串

题目描述 :
输入两个字符串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
#include
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;
 } 

你可能感兴趣的:(牛客每日一题,DP)