hdu_2457_DNA repair(AC自动机+DP)

题目连接:hdu_2457_DNA repair

题意:

给你N个字符串,最后再给你一个要匹配的串,问你最少修改多少次,使得这个串不出现之前给的N的字符串

题解:

刚学AC自动机,切这题还真不知道怎么来DP,然后看了一下题解,需要在失败指针那里做文章,这里我们要将trie的每一个节点当作一个状态,然后设dp[i][j]表示考虑到第i个字符,j这个trie节点时的最小修改次数,为什么要这样考虑,因为AC自动机在匹配失败的时候会转向其他的节点,所以这里我们要考虑每一个节点的状态,然后当前节点的子节点如果不存在也要处理一下,就指向这个节点的fail指针,这样我们在后面dp的时候才能保证匹配失败的时候回到fail节点

#include<cstdio>
#include<cstring>
#define F(i,a,b) for(int i=a;i<=b;i++)

inline void up(int &x,int y){if(x>y)x=y;}
const int AC_N=1011,inf=1e8;
struct AC_automation{
    int tr[AC_N][4],cnt[AC_N],Q[AC_N],fail[AC_N],tot;
    int gt(char x){
        if(x=='A')return 0;
        if(x=='G')return 1;
        if(x=='C')return 2;
        if(x=='T')return 3;
    }
    void nw(){cnt[++tot]=0;memset(tr[tot],-1,sizeof(tr[tot]));}
    void init(){tot=-1,fail[0]=-1,nw();}
    void insert(char *s,int x=0){
        for(int len=strlen(s),i=0,w;i<len;x=tr[x][w],i++)
            if(tr[x][w=gt(s[i])]==-1)nw(),tr[x][w]=tot;
        cnt[x]=1;//串尾标记
    }
    void build(int head=1,int tail=0){
        for(Q[++tail]=0;head<=tail;){
            for(int i=0,x=Q[head++],p=-1;i<=3;i++)if(~tr[x][i]){
                if(x==0)fail[tr[0][i]]=0;
                else{
                    for(p=fail[x],fail[tr[x][i]]=0;~p;p=fail[p])
                    if(~tr[p][i]){fail[tr[x][i]]=tr[p][i];break;}
                }//如果他失败指针指向的节点的子节点为危险DNA那么这点的子节点也不能取
                if(cnt[fail[tr[x][i]]])cnt[tr[x][i]]=1;
                Q[++tail]=tr[x][i];
            }else if(x==0)tr[0][i]=0;//不存在的节点指向失败指针的位置
            else tr[x][i]=tr[fail[x]][i];
        }
    }
    int dp[1010][1010];
    int ask(char *s){
        int len=strlen(s),ans=inf;
        F(i,0,len)F(j,0,tot)dp[i][j]=inf;
        dp[0][0]=0;
        F(i,1,len)F(j,0,tot){
            if(cnt[j]||dp[i-1][j]==inf)continue;
            F(k,0,3){
                int nxt=tr[j][k];
                if(cnt[nxt])continue;
                up(dp[i][nxt],dp[i-1][j]+(gt(s[i-1])!=k));
            }
        }
        F(i,0,tot)up(ans,dp[len][i]);
        return ans==inf?-1:ans;
    }
}AC;

char buf[1010];
int main(){
    int n,ic=1;
    while(~scanf("%d",&n),n){
        AC.init();
        F(i,1,n)scanf("%s",buf),AC.insert(buf);
        AC.build();
        scanf("%s",buf);
        printf("Case %d: %d\n",ic++,AC.ask(buf));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值