这道题的思路很简单,就是普通的dp,甚至比起单个字符串判断最大子回文长度还要简单。但是在细节上有相当多的地方需要注意
由于数据量是50所以说可以用一些时间复杂度比较神秘的dp方法,这里我们模仿普通单个字符串回文的求法,设一个四维dp数组,说明a的[l1,r1]区间与b的[l2,r2]区间组成的字符串最大长度是多少
但是这样又出现了新的问题:我们的dp只会积累数值而不会具体的去记录配对方法,所以dp存储最大子串长度行不通。考虑到我们会遍历所有可能的组合方式,我们可以转而存储是否可以组成一个回文串。在遍历的过程中维护ans即可
对于初始条件:如果长度为1那肯定为1,对于状态转移:只有可能四种情况,开头结尾都来自于a或者b,开头a结尾b或者开头b结尾a。如果相等并且有一种方法使得剩余的内部子串也可以组成回文,则更新ans与当前dp。一个很正常的思路
对于实现的过程,有几点是我犯的错误,这至少浪费了我三四个小时,在这里写出来作为参考
1:出于复杂度,我们开四维数组肯定不会用vector,而且我们使用len方法的话,r = l + len - 1的r是有可能成为负数的,要小心负数索引的话:可以使用经典1-based,类似于栈问题我们放入一个哨兵元素防止为空出错,我们对于a和b也可以加上一个哨兵元素
a = "0" + a;
b = "0" + b;
对于r = l + len - 1,最小也只会为0,就不会因为负数索引而产生神秘错误了
2:书接上文,我们使用了哨兵元素,那么实际长度会+1,这对于我们的四层循环会产生影响,对于len而言,长度需要在.size()的基础上减一。对于枚举左侧元素l,当然r为l + len - 1,但是考虑到1-based,边界条件应该为l + len - 1 <= .size() - 1(这里的size比起原来的1-based而言也还是大了,我一开始就是这里出现问题的)
AC代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
//哇子回文串问题耶。但好像也不是那么板子,可能要找到新的dp方式。。。。。。
//50*50的数据量,又加上dp,可以用一点很神秘的时间复杂度
signed main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int T;
cin >> T;
for(int t = 0;t < T;t++)
{
string a,b;
cin>>a>>b;
a = "0" + a;
b = "0" + b;
int ans = 1;
int n = max(a.size(),b.size());
int dp[60][60][60][60];
memset(dp,0,sizeof(dp));
//对于dp的状态转移,由用户的遍历顺序来负责
for(int len1 = 0;len1 < a.size();len1++)
{
for(int len2 = 0;len2 < b.size();len2++)
{
for(int l1 = 1;l1 + len1 <= a.size();l1++)
{
for(int l2 = 1;l2 + len2 <= b.size();l2++)
{
int r1 = l1 + len1 - 1;
int r2 = l2 + len2 - 1;
if(len1 + len2 <= 1)
{
dp[l1][r1][l2][r2] = 1;
continue;
}
if(a[l1] == a[r1])
{
if(dp[l1 + 1][r1 - 1][l2][r2] == 1)
{
dp[l1][r1][l2][r2] = 1;
ans = max(ans,len1 + len2);
}
}
if(b[l2] == b[r2])
{
if(dp[l1][r1][l2 + 1][r2 - 1] == 1)
{
dp[l1][r1][l2][r2] = 1;
ans = max(ans,len1 + len2);
}
}
if(a[l1] == b[r2])
{
if(dp[l1 + 1][r1][l2][r2 - 1] == 1)
{
dp[l1][r1][l2][r2] = 1;
ans = max(ans,len1 + len2);
}
}
if(b[l2] == a[r1])
{
if(dp[l1][r1 - 1][l2 + 1][r2] == 1)
{
dp[l1][r1][l2][r2] = 1;
ans = max(ans,len1 + len2);
}
}
}
}
}
}
cout<<ans<<endl;
}
return 0;
}