POJ 2778 DNA Sequence AC自動機+DP+快速冪

http://poj.org/problem?id=2778


題意:

給n個病毒基因 長度不超過10

要你構造一個長度爲n的字符串,不能包含任何病毒基因

求方案數


用病毒基因構建ac自動機。

考慮節點作爲一個狀態,初始狀態是在根節點。

建立二維矩陣,

(dp[i][j]表示從狀態節點i走一部能到達狀態節點j的方案數

(根據ac自動機建立一個sz*sz大小的矩陣

(判斷能否到達就看trie[i][j]這個節點是否爲結束標記


那麼初始矩陣A的dp[i][j]就是i節點 經過一步  到達j節點的方案數 

顯然A^2 中的dp[i][j]表示 i 經過2步到達 j的 方案數 

因此求個A^k

然後我們看根節點0 能到達其他節點的方案數 之和就是答案

即表示 根節點出發 走了n步,最後以 其他節點爲結尾的方案數 之和。






#include<bits/stdc++.h>
using namespace std;
const int N=10005;
const int maxlen=1000132;
const int maxn=10005*50;
const int all_size=26;
int trie[maxn][all_size];
int fail[maxn];
int tag[maxn];
int sz;
queue<int >Q;

struct Aho
{
    int root=0;
    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[])   //插入字符串構建ac自動機,構建trie樹
    {
        int len=strlen(s),p=0;;
        for (int i=0; i<len; i++)
        {
            int id=s[i]-'a';
            if (trie[p][id]==-1)
                trie[p][id]=newnode();
            p=trie[p][id];
        }
        tag[p]++;   //結束標記
    }
    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]節點對應的子節點
                {
                    faitrie[u][i]]=trie[fail[u]][i];
                    Q.push(trie[u][i]);     //繼續擴展
                }
            }
        }
    }
} aho;

char ss[55];
char s[maxlen];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        aho.init();
        int n;
        cin>>n;
        for (int i=0; i<n; i++)
        {
            scanf("%s",ss);
            aho.insert(ss);
        }
        aho.getfail();
        scanf("%s",s);
        int len=strlen(s);
        int p=aho.root;
        int ans=0;
        for (int i=0; i<len; i++)
        {
            int idx=s[i]-'a';
             p=trie[p][idx];
             int tmp=p; //取出當前字符在自動機上對應的節點
             while(tmp!=aho.root)//如果有fail節點則一直往上跳,直到跳回root表示沒有了fail後


              {
                  ans+=tag[tmp];//累計答案
                  tag[tmp]=0;//清空tag標記
                  tmp=fail[tmp];//跳到fail後繼節點
              }
        }
        printf("%d\n",ans);

    }
    return 0;
}




















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