CF49E Common ancestor 区间DP

一.题目

传送门
翻译:
Berland的每一个生物的DNA序列可以被表示成一个由小写字母组成的非空字符串。Berland的科学家们发现所有生物都是一步一步进化来的。在其中的每一步,DNA序列的一个字符会被替换成另外的两个。总共有nn种允许的变化。变化aibicia_{i}\rightarrow b_{i}c_{i}表示一个字符aia_i可以被替换成两个字符bicib_{i}c_{i}每一种变化均可以无限次发生。 科学家们表示,如果一个DNA序列s3s_3在整个进化过程中可以最终变为s1s_1s2s_2 或许是经过了不同的步骤数,那么DNA序列分别为s1s_1s2s_2的两个生物就会有一个共同祖先。现在给出s1s_1s2s_2,你的任务是弄清楚分别拥有这两种DNA序列的生物是否有共同祖先。如果存在,你需要找出所有共同祖先中长度最短的DNA序列。

二.题解

这道题的数据范围为0n500\leq n \leq 50所以必定很暴力,先考虑一个O(n4)O(n^4)的算法:

dp[i][j]s1is2jdp[len1][len2]定义dp[i][j]表示s_1的前i个字母与s_2的前j个字母缩成相同的一串字母的最短长度,答案就为dp[len1][len2]

然后考虑转移:dp[i][j]=min(dp[i][j],dp[ik1][jk2]+1)(k1k2s1s2jk1k2)dp[i][j] = min (dp[i][j], dp[i - k1][j - k2] + 1)(k1,k2表示s1和s2的从j开始分别连续k1和k2的长度可以缩成同一字母)
方程式就是这样,下面还有一个预处理,处理出s1s2lr1llen1,len2,1rlen1,len2s_1和s_2中l到r可以缩成的字母(1\leq l \leq len1,len2,1\leq r \leq len1,len2),这是一个时间复杂度为O(n3262)O(n^326^2)的预处理,也很暴力,我们把这个预处理存在一个数组f[i][j][k]f[i][j][k]里。

f[i][j][k]ijk=0s1k=1s2f[i][j][k]存i到j中的字母能够缩成的某个字母,k=0则是s_1,k=1则是s_2

我们要用二进制来存这些字母,226=671088642^26=67108864,用int就行。
这道题就做完了。

三.Code

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

#define M 55
#define INF 0x3f3f3f3f

int n, len1, len2, mp[M][M], f[M][M][5], dp[M][M];
char s1[M], s2[M];

void prepare (char s[], int len, int type){
    for (int i = 1; i <= len; i ++)
        f[i][i][type] |= 1 << (s[i] - 'a');
    for (int r = 1; r <= len; r ++){
        for (int l = r - 1; l >= 1; l --){
            for (int k = l; k < r; k ++){
                for (int i = 0; i < 26; i ++){
                    if (! (f[l][k][type] >> i & 1))
                        continue;
                    for (int j = 0; j < 26; j ++){
                        if (! (f[k + 1][r][type] >> j & 1))
                            continue;
                        f[l][r][type] |= mp[i][j];
                    }
                }
            }
        }
    }
}
int main (){
    scanf ("%s%s", s1 + 1, s2 + 1);
    len1 = strlen (s1 + 1);
    len2 = strlen (s2 + 1);
    scanf ("%d", &n);
    char tmp[10];
    for (int i = 1; i <= n; i ++){
        scanf ("%s", tmp);
        mp[tmp[3] - 'a'][tmp[4] - 'a'] |= 1 << (tmp[0] - 'a');
    }
    prepare (s1, len1, 0);
    prepare (s2, len2, 1);
    for (int i = 0; i <= len1; i ++)
        for (int j = 0; j <= len2; j ++)
            dp[i][j] = INF;
    dp[0][0] = 0;
    for (int i = 1; i <= len1; i ++){
        for (int j = 1; j <= len2; j ++){
            for (int k1 = 1; k1 <= i; k1 ++){
                for (int k2 = 1; k2 <= j; k2 ++){
                    if (! (f[k1][i][0] & f[k2][j][1]))
                        continue;
                    if (dp[k1 - 1][k2 - 1] == INF)
                        continue;
                    dp[i][j] = min (dp[i][j], dp[k1 - 1][k2 - 1] + 1);
                }
            }
        }
    }
    if (dp[len1][len2] == INF)
        printf ("-1\n");
    else
        printf ("%d\n", dp[len1][len2]);
    return 0;
}

谢谢!

posted on 2020-07-29 11:20  PI_PJW  阅读(79)  评论(0)    收藏  举报

导航