【POJ3691】DNA repair (AC自动机+DP)

版权声明:本文为博主原创文章,请随意转载(注明出处)。 https://blog.csdn.net/can919/article/details/81584628

题意

给定N个模式字符串,和一个匹配串,要求匹配串中不出现任何一个模式串,最少修改几个字母?
(字符串中只有’A’、’C’、’G’、’T’,修改操作也只能修改成这四个字母)

题解

简单的AC自动机+DP,适合作为模板题

(不知道这中算法的人)首先想到的就是最暴力的DP就是:dp[i][j]表示当前在第i位,第i位为j字符时,最少需要修改多少次,每次转移到i就必须检查i之前的若干位是否为模式串;
而AC自动机就能优化这一缺点
将dp定义为dp[i][j]表示当前在第i位,匹配到AC自动机上的第j个结点
转移时,考虑添加一个字符,在AC自动机上获取添加这个结点会转移到的下一个结点(字符串匹配),并判断这样转移是否形成了一个模式串。
dp[i+1][u->由字符k转移到 v]=dp[i][u]+(字符k == str[i+1])

代码

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define charID(x) (x=='A'?0:(x=='G'?1:(x=='C'?2:3)))
const int MAXN=53,MAXL1=22,MAXL2=1005,MAXCHAR=4;

//dp[i][j]+ * -> dp[i+1][nxt[j]['A''G''C''T']]

struct Node
{
    int val;
    Node *son[MAXCHAR],*fail;
    void Clear()
    {memset(this,0,sizeof(Node));}
};

Node nodes[MAXN*MAXL1],*root=nodes,*ntop=nodes+1;
void Clear()
{
    root=nodes;
    root->Clear();
    ntop=nodes+1;
}
int ID(Node *u)
{return u-nodes;}
void Insert(const char *str)
{
    Node *u=root;
    for(int i=0;str[i];i++)
    {
        int c=charID(str[i]);
        if(u->son[c]==NULL)
        {
            u->son[c]=ntop++;
            u->son[c]->Clear();
        }
        u=u->son[c];
    }
    u->val=1;
}
void Build()
{
    static queue<Node*> Q;
    for(int i=0;i<MAXCHAR;i++)
        if(root->son[i])
        {
            root->son[i]->fail=root;
            Q.push(root->son[i]);
        }
    while(!Q.empty())
    {
        Node *u=Q.front();
        Q.pop();
        for(int i=0;i<MAXCHAR;i++)
            if(u->son[i])
            {
                Node *v=u->son[i],*f=u->fail;
                Q.push(v);
                while(f&&f->son[i]==NULL)
                    f=f->fail;
                if(f)
                    v->fail=f->son[i];
                else
                    v->fail=root;
                v->val|=v->fail->val;
            }
    }
}
Node *GetNext(Node *u,int c)
{
    while(u&&u->son[c]==NULL)
        u=u->fail;
    if(u)
        return u->son[c];
    return root;
}

char s[MAXL1],str[MAXL2];
int dp[MAXL2][MAXN*MAXL1];

int main()
{
    for(int test=1;;test++)
    {
        int n,m;
        scanf("%d",&n);
        if(n==0)
            break;
        Clear();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            Insert(s);
        }
        Build();
        scanf("%s",str+1);
        m=strlen(str+1);
        memset(dp,0x3F,sizeof dp);
        dp[0][0]=0;
        for(int i=0;i<m;i++)
            for(int j=0;nodes+j<ntop;j++)
                if(dp[i][j]<0x3F3F3F3F)
                {
                    Node *u=nodes+j;
                    for(int c=0;c<MAXCHAR;c++)
                    {
                        Node *v=GetNext(u,c);
                        if(v->val)
                            continue;
                        dp[i+1][ID(v)]=min(dp[i+1][ID(v)],dp[i][j]+(c!=charID(str[i+1])));
                    }
                }
        int ans=0x3F3F3F3F;
        for(int j=0;nodes+j<ntop;j++)
            ans=min(ans,dp[m][j]);
        if(ans==0x3F3F3F3F)
            ans=-1;
        printf("Case %d: %d\n",test,ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/can919/article/details/81584628
今日推荐