HDU 2457 DNA repair(AC自動機+DP)題解

題意:給你幾個模式串,問你主串最少改幾個字符能夠使主串不包含模式串

思路:從昨天中午開始研究,研究到現在終於看懂了。既然是多模匹配,我們是要用到AC自動機的。我們把主串放到AC自動機上跑,並保證不出現模式串,這裏對AC自動機的創建有所改動,我們需要修改不存在但是符合要求的節點,如果某節點的某一子節點不存在,我們就把這個子節點指向他父輩節點存在的該節點(比如k->next[1]不存在,k->fail->next[1]存在,我們就把他改爲k->next[1] = k->fail->next[1]),因爲只是在AC自動機上跑,我們只關心會不會匹配到模式串。這裏要當心,如果一個節點的失配指向的是模式串尾,那麼這個節點也就是模式串尾。我們用dp[i][j]去儲存當前匹配到主串第i位,跑到AC自動機第j個狀態的最小修改,狀態轉移方程dp[i + 1][j-next] = min(dp[i][j->next],dp[i][j]+str[i]!=k),每次我們都從上一個狀態j開始搜索滿足要求的下一個狀態j->next。

參考:

[Pku 3691 1625] 字符串(四) {自動機應用}

HDU 2457 DNA repair(AC自動機+DP)

代碼:

#include<cstdio>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define ll long long
const int maxn = 1000+5;
const int maxm = 100000+5;
const int MOD = 1e7;
const int INF = 0x3f3f3f3f;
const int kind = 4;
const char baset = 0;
using namespace std;
struct Trie{
    Trie *next[kind];
    Trie *fail;
    int flag;
    int id;
};
Trie *root,point[maxn];
queue<Trie*> Q;
int head,tail,idx;
int ID(char ch){
    if(ch == 'A') return 0;
    else if(ch == 'C') return 1;
    else if(ch == 'T') return 2;
    return 3;
}
Trie* NewNode(){
    Trie *temp = &point[idx];
    memset(temp ->next,NULL,sizeof(temp ->next));
    temp ->flag = 0;
    temp ->id = idx++;
    temp ->fail = NULL;
    return temp;
}

void Insert(char *s){
    Trie *p = root;
    for(int i = 0;s[i];i++){
        int x = ID(s[i]);
        if(p ->next[x] == NULL){
            p ->next[x] = NewNode();
        }
        p = p ->next[x];
    }
    p ->flag = 1;
}
void del(Trie *p){
    if(p == NULL) return;
    for(int i = 0;i < kind;i++){
        if(p ->next[i])
            del(p ->next[i]);
    }
    delete p;
}
void buildFail(){
    while(!Q.empty()) Q.pop();
    Q.push(root);
    Trie *p,*temp;
    while(!Q.empty()){
        temp = Q.front();
        Q.pop();
        for(int i = 0;i < kind;i++){
            if(temp ->next[i]){
                if(temp == root){
                    temp ->next[i] ->fail = root;
                }
                else{
                    p = temp ->fail;
                    while(p){
                        if(p ->next[i]){
                            temp ->next[i] ->fail = p ->next[i];
                            break;
                        }
                        p = p ->fail;
                    }
                    if(p == NULL) temp ->next[i] ->fail = root;
                }
                if(temp ->next[i] ->fail ->flag)
                    temp ->next[i] ->flag = 1;
                    //這裏要注意,如果一個節點的失配指向的是模式串尾
                    //那麼這個節點也就是模式串尾
                Q.push(temp ->next[i]);
            }
            else if(temp == root)
                temp ->next[i] = root;
                //root指向root,因爲不存在這個子節點,沒有限制
            else
                temp ->next[i] = temp ->fail ->next[i];
                //指向長輩節點存在的這個子節點
        }
    }
}
int dp[maxn][maxn << 1]; //主串匹配到第i個,在AC自動機上走到第j個節點
int solve(char *ch){
    int len = strlen(ch);
    for(int i = 0;i <= len;i++)
        for(int j = 0;j <= idx;j++)
            dp[i][j] = INF;
    dp[0][0] = 0;
    for(int i = 1;i <= len;i++){
        for(int j = 0;j < idx;j++){ //從這個狀態j開始走,dp下一個狀態
            if(point[j].flag) continue; //這個狀態包含模式串
            if(dp[i - 1][j] == INF) continue;
            for(int k = 0;k < 4;k++){
                int r = point[j].next[k] ->id;  //下一個點
                if(point[r].flag) continue;     //下一個點包含模式串
                dp[i][r] = min(dp[i][r],dp[i - 1][j] + (ID(ch[i - 1]) != k));
            }
        }
    }
    int ans = INF;
    for(int i = 0;i < idx;i++){
        ans = min(ans,dp[len][i]);
    }
    if(ans == INF) return -1;
    else return ans;
}
char ch[1005];
int main(){
    int n,m,x,Case = 1;
    while(scanf("%d",&n) != EOF && n){
        idx = 0;
        root = NewNode();
        for(int i = 0;i < n;i++){
            scanf("%s",ch);
            Insert(ch);
        }
        buildFail();
        scanf("%s",ch);
        printf("Case %d: %d\n",Case++,solve(ch));
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章