HDU 2457 DNA repair AC自动机+DP

http://acm.hdu.edu.cn/showproblem.php?pid=2457

题意: 给出了一些病毒串的基因, 又给出了一个基因串,问至少修改多少个基因串中的字符可以不含病毒串。


首先把病毒建立一个自动机。

dp[i][j]表示 长度为i的字符串以状态j结尾时,最少修改数

dp[0][0]=0,其他无穷


那么dp[i][j]可以用 dp[i-1][k]也就是长度为i-1时,状态为k的节点转移过来,

转移的条件是,k本身不是病毒节点,且k的后继节点j也不是病毒节点


最后扫描dp[n][i],如果为inf表示无法转到此状态

取min值即是答案


dp为1000*1000

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;

const int maxlen=1000+50;
const int maxn=1005;
const int all_size=4;
int trie[maxn][all_size];
int fail[maxn];
int tag[maxn];

int sz;
queue<int >Q;
int n;
struct Aho
{
    int root;
    int newnode()//静态创建新节点
    {
        memset(trie[sz],-1,sizeof trie[sz]);
        tag[sz]=0;
        sz++;
        return sz-1;
    }
    void init()//初始化
    {
        sz=0;
        newnode();
    }
    void insert(char s[],int id)   //插入字符串构建ac自动机,构建trie树
    {
        int len=strlen(s),p=0;;
        for (int i=0; i<len; i++)
        {
            int id=s[i]-'0';
            if (trie[p][id]==-1)
                trie[p][id]=newnode();
            p=trie[p][id];
        }
        tag[p]=id;   //结束标记
    }
    void getfail() //构建自动机fail指针
    {
        while(!Q.empty()) Q.pop();
        fail[root]=root;            //root指向root
        for (int i=0; i<all_size; i++)
        {
            if (trie[root][i]==-1)//第一个字符不存在,指向root
                trie[root][i]=root;
            else    //第一个字符的fail指针指向root
            {
                fail[trie[root][i]]=root;
                Q.push(trie[root][i]);   //并放入队列,待bfs扩展
            }
        }
        while(!Q.empty())
        {
            int u=Q.front();    //取扩展节点
            Q.pop();
            if(tag[fail[u]])     tag[u]=1;  //***如果之前是tag,直接标记
            for (int i=0; i<all_size; i++)//遍历所有子节点
            {
                if (trie[u][i]==-1)//如果不存在,则子节点直接指向fail[u]节点的对应子节点
                    trie[u][i]=trie[fail[u]][i];
                else     //如果存在,则该节点的fail指针指向fail[u]节点对应的子节点
                {
                    fail[trie[u][i]]=trie[fail[u]][i];
                    Q.push(trie[u][i]);     //继续扩展
                }
            }
        }
    }

} aho;
char ss[maxlen];
char cop[maxlen];
char name[55][25];
void change(char *ss)
{
    int len=strlen(ss);
    for (int i=0; i<len; i++)
        if (ss[i]=='A') ss[i]='0'+0;
        else     if (ss[i]=='T')ss[i]='0'+1;
        else     if (ss[i]=='C')ss[i]='0'+2;
        else     if (ss[i]=='G')ss[i]='0'+3;
}
int dp[1005][1005];
const int inf=1e6;
int main()
{

    int cnt=1;
    while(cin>>n&&n)
    {
        aho.init();
        for (int i=0; i<n; i++)
        {
            scanf("%s",name[i]);
            int len=strlen(name[i]);
            change(name[i]);
            aho.insert(name[i],i+1);
        }

        aho.getfail();

        scanf("%s",ss);
        change(ss);
        int len=strlen(ss);

        for (int i=0; i<=len; i++)
            for (int j=0; j<sz; j++)
                dp[i][j]=inf;


        dp[0][0]=0;
        for (int L=1; L<=len; L++)
        {
            for (int i=0; i<sz; i++)
            {
                if (tag[i])continue;
                if (dp[L-1][i]==inf) continue;
                int id=ss[L-1]-'0';
                for (int j=0; j<all_size; j++)
                {
                    if (tag[trie[i][j]])continue;
                    int add=(j!=id);
                    dp[L][trie[i][j]]=min(dp[L][trie[i][j]],
                                          dp[L-1][i]+add);
                }
                int cc;
            }
        }
        int ans=inf;
        for (int i=0; i<sz; i++)
            ans=min(ans,dp[len][i]);


        printf("Case %d: ",cnt++);
        if (ans==inf)
            printf("-1\n");
        else
            printf("%d\n",ans);


    }
    return 0;
}
















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值