牛客NC13230:合并回文子串

合并回文子串

题目链接

题目描述

输入两个字符串A和B,合并成一个串C,属于A和B的字符在C中顺序保持不变。如"abc"和"xyz"可以被组合成"axbycz"或"abxcyz"等。
我们定义字符串的价值为其最长回文子串的长度(回文串表示从正反两边看完全一致的字符串,如"aba"和"xyyx")。
需要求出所有可能的C中价值最大的字符串,输出这个最大价值即可

输入描述:
第一行一个整数T(T ≤ 50)。
接下来2T行,每两行两个字符串分别代表A,B(|A|,|B| ≤ 50),A,B的字符集为全体小写字母。

输出描述:
对于每组数据输出一行一个整数表示价值最大的C的价值。

示例1

输入
2
aa
bb
a
aaaabcaa

输出
4
5

这是一道区间DP的问题,感觉很多回文串的问题都是运用DP然后考虑头尾两个元素的值的关系然后确定转移方程。这题也不例外了,但是DP的含义和DP的方式有些特别。首先设DP[i][j][k][l]代表从a串中取第i个元素到第j个元素,然后在b中取第k个元素到第l个元素,看是否能组成回文串,可以为1,不能则为0,这里有一点要注意,不是从这两个范围里面分别选若干个元素去组合回文串,是要把范围内每个都选上,若是选若干个,那么只要在a或b中选一个字母,然后另一个不选,那么这必能构成回文串,所以那样的DP的含义就没有什么意义,无法深入到具体情况,而且对于同一个串而言,选择的字母必须要连续,因为题目表明“属于A和B的字符在C中顺序保持不变”,所以一定是连续的取范围中的每一个。然后就是转移方程,转移方程的思路就是考虑头尾,因为取的范围我们知道,那么头尾的组合只有四种情况(前头后尾)(1):a[i] a[j](2):a[i] b[l](3):b[k] b[l](4):b[k] a[j]。然后我们就可以得到下列转移方程(先忽略里面的x和y,这是和转移方式有关的参数):

                        if(a[i-1]==a[j-1]&&x>1)
                            dp[i][j][k][l]|=dp[i+1][j-1][k][l];
                        if(b[k-1]==b[l-1]&&y>1)
                            dp[i][j][k][l]|=dp[i][j][k+1][l-1];
                        if(a[i-1]==b[l-1]&&x&&y)
                            dp[i][j][k][l]|=dp[i+1][j][k][l-1];
                        if(a[j-1]==b[k-1]&&x&&y)
                            dp[i][j][k][l]|=dp[i][j-1][k+1][l];

那么注意,里面用的不是等于,是或与,为什么呢?因为每种情况只是对应于一种摆放头尾的情况,所以一种不行不代表其他的不行,但是如果我已经判断到一种情况可以就想不要被其他情况影响,所以用或与可以完美适配这个问题,当然,其他的写法肯定是可以的,只是或与看起来比较简洁。如果一个条件都不满足的话就肯定是不行了,因为所有可能的头尾的情况都无法满足,那么就一定无法构成回文串了,当DP里面元素初始化为0的话就自然就不用处理了这种情况了。

那么我们要求的是长度,乍一眼一看好像很简单,我们是可以知道我们两个子串具体取到哪个范围的,所以具体有多少个字符是可以通过i,j,k,l中算出来的,但是这里我们要和转移方程结合来看,我们转移的时候一定要保证等号右边是要已经求出来了的。所以我们通过观察可以发现,对于求dp[i][j][k][l]来说,等号右边都是a或者b长度缩减了的情况,所以我们只要对长度去DP就好了,那么也就是上面的代码中的x和y了,里面所有关于x和y的判断条件其实都是一个含义:若是两个串一共只取一个元素就直接是回文串,其他情况至少两个元素才能构成头尾

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int dp[52][52][52][52];
int main()
{
    int T;
    cin>>T;
    string a,b;
    while(T--)
    {
        int ans=0;
        memset(dp,0,sizeof(dp));
        cin>>a>>b;
        int lena=a.length(),lenb=b.length();
        for(int x=0;x<=lena;++x)
            for(int y=0;y<=lenb;++y)
                for(int i=1,j=x;j<=lena;++i,++j)
                    for(int k=1,l=y;l<=lenb;++k,++l)
                    {
                        if(x+y<=1)
                            dp[i][j][k][l]=1;
                        else
                        {
                        if(a[i-1]==a[j-1]&&x>1)
                            dp[i][j][k][l]|=dp[i+1][j-1][k][l];
                        if(b[k-1]==b[l-1]&&y>1)
                            dp[i][j][k][l]|=dp[i][j][k+1][l-1];
                        if(a[i-1]==b[l-1]&&x&&y)
                            dp[i][j][k][l]|=dp[i+1][j][k][l-1];
                        if(a[j-1]==b[k-1]&&x&&y)
                            dp[i][j][k][l]|=dp[i][j-1][k+1][l];
                        }
                        if(dp[i][j][k][l])
                            ans=max(ans,x+y);
                    }
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值