POJ 2778 DNA Sequence AC自动机+DP+快速幂

来源:互联网 发布:淘宝关键词的大中小词 编辑:程序博客网 时间:2024/05/31 13:16

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;}






0 0