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