AC自动机+状态机dp——修复DNA

修复DNA

生物学家终于发明了修复DNA的技术,能够将包含各种遗传疾病的DNA片段进行修复。

为了简单起见,DNA看作是一个由’A’, ‘G’ , ‘C’ , ‘T’构成的字符串。

修复技术就是通过改变字符串中的一些字符,从而消除字符串中包含的致病片段。

例如,我们可以通过改变两个字符,将DNA片段”AAGCAG”变为”AGGCAC”,从而使得DNA片段中不再包含致病片段”AAG”,”AGC”,”CAG”,以达到修复该DNA片段的目的。

需注意,被修复的DNA片段中,仍然只能包含字符’A’, ‘G’ , ‘C’ , ‘T’。

请你帮助生物学家修复给定的DNA片段,并且修复过程中改变的字符数量要尽可能的少。

输入格式
输入包含多组测试数据。

每组数据第一行包含整数N,表示致病DNA片段的数量。

接下来N行,每行包含一个长度不超过20的非空字符串,字符串中仅包含字符’A’, ‘G’ , ‘C’ , ‘T’,用以表示致病DNA片段。

再一行,包含一个长度不超过1000的非空字符串,字符串中仅包含字符’A’, ‘G’ , ‘C’ , ‘T’,用以表示待修复DNA片段。

最后一组测试数据后面跟一行,包含一个0,表示输入结束。

输出格式
每组数据输出一个结果,每个结果占一行。

输入形如”Case x: y”,其中x为测试数据编号(从1开始),y为修复过程中所需改变的字符数量的最小值,如果无法修复给定DNA片段,则y为”-1”。

数据范围
1 N 50 1≤N≤50
输入样例:
2
AAA
AAG
AAAG
2
A
TG
TGAATG
4
A
G
C
T
AGT
0
输出样例:
Case 1: 1
Case 2: 4
Case 3: -1

题解:

先定义dp函数, f ( i , j ) f(i,j) 代表我们匹配到第i个位置,并且以tril树上第j个位置结尾的答案。所以我们在跳到可以匹配到的后缀的位置之前来枚举当前的位置的字母来统计答案,如果当前位置和病毒对应的当前位置是不同的则可以+1否则不能。

#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int ne[N],tar[N],tr[N][5],cnt,n;
char str[N];
int f[N][N];
queue<int> q;
int get(char c)
{
    if (c == 'A') return 0;
    if (c == 'T') return 1;
    if (c == 'G') return 2;
    return 3;
}
void insert()
{
    int p=0;
    for(int i=0;str[i];i++){
        int t=get(str[i]);
        if(!tr[p][t]) tr[p][t]=++cnt;
        p=tr[p][t];
    }
    tar[p]=1;
}
void build()
{
    for(int i=0;i<4;i++) if(tr[0][i]) q.push(tr[0][i]);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=0;i<4;i++){
            int p=tr[u][i];
            if(!p) tr[u][i]=tr[ne[u]][i];
            else {
                ne[p]=tr[ne[u]][i];
                q.push(p);
                tar[p]|=tar[ne[p]];
            }
        }
    }
}
int main()
{
    int T = 1;
    while (scanf("%d", &n), n)
    {
        memset(tr, 0, sizeof tr);
        memset(tar, 0, sizeof tar);
        memset(ne, 0, sizeof ne);
        cnt = 0;
        for (int i = 0; i < n; i ++ )
        {
            scanf("%s", str);
            insert();
        }
        build();
        scanf("%s", str + 1);
        int m = strlen(str + 1);
        memset(f, 0x3f, sizeof f);
        f[0][0] = 0;
        for (int i = 0; i < m; i ++ )
            for (int j = 0; j <= cnt; j ++ )
                for (int k = 0; k < 4; k ++ ){
                    int t = get(str[i + 1]) != k;
                    int p = tr[j][k];
                    if (!tar[p]) f[i + 1][p] = min(f[i + 1][p], f[i][j] + t);
                }

        int res = 0x3f3f3f3f;
        for (int i = 0; i <= cnt; i ++ ) res = min(res, f[m][i]);
        if (res == 0x3f3f3f3f) res = -1;
        printf("Case %d: %d\n", T ++, res);
    }
    return 0;
}
发布了181 篇原创文章 · 获赞 10 · 访问量 5057

猜你喜欢

转载自blog.csdn.net/weixin_42979819/article/details/104227103
今日推荐