[区间dp] NC13230合并回文子串

题面

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

分析

  若是这题暴力把所有合成的 C 串找出来,则共有 C 100 50 C_{100}^{50} C10050 种可能,这种指数复杂度是不能承受的。而类比找一个字符串的回文串,若是只是单个字符串,我们可以通过dp进行 O ( n 2 ) O(n^2) O(n2) 的算法:

//字符串从s[1]开始存储,长度为len
for(int L = 1; L <= len; L++)
{
    for(int i = 1; i + L  - 1 <= len; i++)
    {
        int j = i + L - 1;             
        if(s[i] == s[j] && (L <= 2 || dp[i+1][j-1]))
        {
            dp[i][j] = 1;
            ans = max(ans, L);
        }
    }
}

  若是要用动态规划,区间dp的思想是考虑从小规模的状态转移到大规模的状态,因此肯定是从长度较小的回文子串转移到长度较大的回文子串,即如果一个回文串首尾加上同样的字母,可以构成一个新的更长的回文串。
  若是用 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示区间 [ i , j ] [i, j] [i,j] 构成的子串是不是回文串(01表示),那么一个区间是回文串有以下三种可能:
  ① i = j i = j i=j,即单个字符是回文串;
  ② i = j − 1 i = j - 1 i=j1 并且 s [ i ] = s [ j ] s[i] = s[j] s[i]=s[j],即两个一样的字符是回文串;
  ③ i < j − 1 i < j - 1 i<j1 并且 s [ i ] = s [ j ] ,   d p [ i + 1 ] [ j − 1 ] = 1 s[i] = s[j],\ dp[i+1][j-1] = 1 s[i]=s[j] dp[i+1][j1]=1,即一个回文串首尾加上同样的字母,可以构成一个新的更长的回文串。
  因此若是求单个字符串的回文子串,大循环是区间长度,小循环是区间起始位置进行双重循环就行,当然还有更优秀的马拉车算法( O ( n ) O(n) O(n)) 。
  
  对于这一题,我们需要考虑的是两个字符串组合得到的回文串,则需要四维 d p [ i ] [ k ] [ j ] [ l ] dp[i][k][j][l] dp[i][k][j][l], 表示 A [ i ] [ k ] , B [ j ] [ l ] A[i][k], B[j][l] A[i][k],B[j][l] 是否可以组成一个回文串。记 l e n 1 = k − i + 1 ,   l e n 2 = l − j + 1 len_1 = k - i + 1, \ len_2 = l - j + 1 len1=ki+1 len2=lj+1, 同样地,我们需要考虑状态转移:
  ①如果 l e n 1 + l e n 2 ≤ 1 len_1 + len_2 ≤ 1 len1+len21, 则 d p [ i ] [ k ] [ j ] [ l ] dp[i][k][j][l] dp[i][k][j][l] 为 1。等于 1 时两个字符串的截取一个为空,一个长度为1,自然是回文串;等于 0 时两个字符串都截取为空,其实是没有回文串的定义的,但是为了之后的转移方便(如从 d p [ 0 ] [ 0 ] [ 0 ] [ 0 ] dp[0][0][0][0] dp[0][0][0][0] 转移到 d p [ 1 ] [ 1 ] [ 1 ] [ 1 ] dp[1][1][1][1] dp[1][1][1][1]), 定义为1。
  ②如果 l e n 1 > 1 len_1 > 1 len1>1, 那么对于 d p [ i ] [ k ] [ j ] [ l ] dp[i][k][j][l] dp[i][k][j][l], 若 A [ i ] = A [ k ] A[i] = A[k] A[i]=A[k] d p [ i + 1 ] [ k − 1 ] [ j ] [ l ] = 1 dp[i+1][k-1][j][l] = 1 dp[i+1][k1][j][l]=1,那么首尾加上 A [ i ] , A [ k ] A[i] ,A[k] A[i]A[k]还是回文串,则 d p [ i ] [ k ] [ j ] [ l ] = 1 dp[i][k][j][l] = 1 dp[i][k][j][l]=1 。同理对于 l e n 2 > 1 len_2 > 1 len2>1
  ③如果 l e n 1 > 0 len_1 > 0 len1>0 l e n 2 > 0 len_2 > 0 len2>0, 若 A [ i ] = B [ l ] A[i] = B[l] A[i]=B[l] d p [ i + 1 ] [ k ] [ j ] [ l − 1 ] = 1 dp[i+1][k][j][l-1] = 1 dp[i+1][k][j][l1]=1, 那么在首尾加上 A [ i ] , B [ l ] A[i], B[l] A[i],B[l] 还是回文串,则 d p [ i ] [ k ] [ j ] [ l ] = 1 dp[i][k][j][l] = 1 dp[i][k][j][l]=1 。同理对于 A [ k ] = B [ j ] A[k] = B[j] A[k]=B[j] d p [ i ] [ k − 1 ] [ j + 1 ] [ l ] = 1 dp[i][k-1][j+1][l] = 1 dp[i][k1][j+1][l]=1
  
  所以以上就得出了从小回文串转移到大回文串的区间动态规划过程。通过四重循环,前两重为A, B串截取的长度,后两重为 A, B串的区间起始位置,在计算时记录最大值就可以解决了。
  

代码

#include <bits/stdc++.h>

using namespace std;
typedef unsigned long long ll;
const int maxn = 1005;
const int INF = 0x3f3f3f3f;
const ll mod = 998244353;

char a[52], b[52];
int f[52][52][52][52], len1, len2, ans;

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        ans = 1;
        scanf("%s %s", a + 1, b + 1);

        len1 = strlen(a + 1), len2 = strlen(b + 1);

        for(int k1 = 0; k1 <= len1; k1++)       //前两重循环区间长度
            for(int k2 = 0; k2 <= len2; k2++)
                for(int i = 1; i + k1 - 1 <= len1; i++)      //后两重循环区间起始位置
                    for(int j = 1; j + k2 - 1 <= len2; j++)
                    {
                        int k = i + k1 - 1, l = j + k2 - 1;
                        if(k1 + k2 <= 1)           //根据状态转移方程得出
                            f[i][k][j][l] = 1;
                        else
                        {
                            f[i][k][j][l] = 0;      //由于有多次询问,因此需要清零防止上次的dp数据干扰
                            if(k1 > 1) f[i][k][j][l] |= (f[i+1][k-1][j][l] && a[i] == a[k]);
                            if(k2 > 1) f[i][k][j][l] |= (f[i][k][j+1][l-1] && b[j] == b[l]);
                            if(k1 && k2) f[i][k][j][l] |= (f[i+1][k][j][l-1] && a[i] == b[l]);
                            if(k1 && k2) f[i][k][j][l] |= (f[i][k-1][j+1][l] && a[k] == b[j]);
                        }

                        if(f[i][k][j][l]) ans = max(ans, k1 + k2);
                    }

        printf("%d\n", ans);
    }
}


  除了转移方程,还需要特别注意动态规划过程边界的处理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值