poj 3691 DNA repair AC自动机 dp

思路:
AC自动机+DP,比较裸的题。先按照病毒串建立AC自动机(Trie图当然也可以),然后设出状态:f[i][j]表示匹配到原串第i个字符时,在AC自动机的j号节点时没有病毒串的最小花费。转移的时候有三种情况:1.转移出病毒串,这个时候直接continue。2.转移与原串相符,这个时候直接将数值转移过去。3.转移与原串不符,这个时候转移数值之后还要+1。
在建立AC自动机的时候千万不要忘记,一个串的自串中有病毒,那么这个串也有病毒。

#include <queue>  
#include <cstdio>  
#include <cstring>  
#include <iostream>  
#include <algorithm>  
#define MAX 1010  
#define INF 0x3f3f3f3f  
using namespace std;  

int cnt;  
struct Trie *tree[MAX];  

struct Trie{  
    Trie *son[4],*fail;  
    int id;  
    bool end;  

    Trie() {  
        memset(son,NULL,sizeof(son));  
        fail = NULL;  
        end = false;  
        id = ++cnt;  
        tree[cnt] = this;  
    }  
}*root = new Trie();  

int cases;  
char s[MAX];  
int f[MAX][MAX];  

inline void Initialize()  
{  
    cnt = 0;  
    root = new Trie();  
    memset(f,0x3f,sizeof(f));  
}  

inline int P(char c)  
{  
    if(c == 'A')    return 0;  
    if(c == 'G')    return 1;  
    if(c == 'C')    return 2;  
    return 3;  
}  

inline void Insert(char *s)  
{  
    Trie *now = root;  
    while(*s != '\0') {  
        if(now->son[P(*s)] == NULL)  
            now->son[P(*s)] = new Trie();  
        now = now->son[P(*s)];  
        ++s;  
    }  
    now->end = true;  
}  

void MakeTrieGraph()  
{  
    static queue<Trie *> q;  
    while(!q.empty())   q.pop();  
    for(int i = 0; i < 4; ++i)  
        if(root->son[i] != NULL)  
            root->son[i]->fail = root,q.push(root->son[i]);  
    while(!q.empty()) {  
        Trie *now = q.front(); q.pop();  
        for(int i = 0; i < 4; ++i)  
            if(now->son[i] != NULL) {  
                Trie *temp = now->fail;  
                while(temp != root && temp->son[i] == NULL)  
                    temp = temp->fail;  
                if(temp->son[i] != NULL)  
                    temp = temp->son[i];  
                now->son[i]->fail = temp;  
                now->son[i]->end |= temp->end;  
                q.push(now->son[i]);  
            }  
            else    now->son[i] = now->fail->son[i] ? now->fail->son[i]:root;  
    }  
}  

void AC_Automaton_DP()  
{  
    f[0][1] = 0;  
    for(int i = 0; i < 4; ++i)  
        if(root->son[i] == NULL)  
            root->son[i] = root;  
    int length = strlen(s);  
    for(int i = 1; i <= length; ++i)  
        for(int j = 1; j <= cnt; ++j)  
            for(int k = 0; k < 4; ++k) {  
                Trie *aim = tree[j]->son[k];  
                if(aim->end) continue;  
                f[i][aim->id] = min(f[i][aim->id],f[i - 1][j] + (P(s[i - 1]) != k));  
            }  
}  

int main()  
{  
    while(scanf("%d",&cases),cases) {  
        Initialize();  
        for(int i = 1; i <= cases; ++i) {  
            scanf("%s",s);  
            Insert(s);  
        }  
        MakeTrieGraph();  
        scanf("%s",s);  
        AC_Automaton_DP();  
        int ans = INF,length = strlen(s);  
        for(int i = 1; i <= cnt; ++i)  
            ans = min(ans,f[length][i]);  
        if(ans == INF)  ans = -1;  
        static int T = 0;  
        printf("Case %d: %d\n",++T,ans);  
    }  
    return 0;  
}  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值