题意:给出m个危险字符串,问有多少个长度为n的字符串不包含以上子串。(仅含A,T,C,G四个字符)
题解:
http://blog.csdn.net/morgan_xww/article/details/7834801(参考的题解)
数字逻辑电路这堂课中的时序逻辑电路设计,有提到状态图。今天发现AC自动机的原理和状态图的设计是差不多的。
把每一个结点当成一个状态,Next[i][j]表示:初始在状态i,输入一个j字符之后,转移到了状态Next[i][j]。
搞清楚了这一点之后,代码中的build()函数便是借用fail指针建立状态图的过程。
对于这道题,还需定义一些节点是危险节点。
建立Trie树时,表示 危险字符串 的结点是危险节点。
建立状态图时,如果一个节点的失败指针指向危险节点,那么它也是危险节点(因为它表示的字符串拥有危险字符串)。
我们用矩阵mat[i][j]表示i到j有几种方案。因为不能到达危险状态,所以我们去掉危险节点表示的行和列。
然后计算pow(mat,n)(用矩阵快速幂优化)。
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <string>
#include <cmath>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define de(x) cout << #x << "=" << x << endl
const int N=205;
const int mod=100000;
map<char,int> mp;
map<int,int> id;
int cnt;
struct Tire {
int Next[N][4],fail[N],End[N];
int root,tot;
int newNode() {
memset(Next[tot],-1,sizeof(Next[tot]));
End[tot]=0;
return tot++;
}
void init() {
tot=0;
root=newNode();
}
void insert(char *buf) {
int len=strlen(buf);
int now=root;
for(int i=0;i<len;++i) {
if(Next[now][mp[buf[i]]]==-1) {
Next[now][mp[buf[i]]]=newNode();
}
now=Next[now][mp[buf[i]]];
}
End[now]++;
}
void build() {
queue<int> q;
fail[root]=root;
for(int i=0;i<4;++i) {
if(Next[root][i]==-1) {
Next[root][i]=root;
} else {
fail[Next[root][i]]=root;
q.push(Next[root][i]);
}
}
while(!q.empty()) {
int now=q.front();q.pop();
for(int i=0;i<4;++i) {
if(Next[now][i]==-1) {
Next[now][i]=Next[fail[now]][i];
} else {
fail[Next[now][i]]=Next[fail[now]][i];
if(End[Next[fail[now]][i]]) End[Next[now][i]]=1;
q.push(Next[now][i]);
}
}
}
}
}ac;
struct Mat {
ll mat[N][N];
Mat () {memset(mat,0,sizeof(mat));}
Mat operator * (Mat B) {
Mat C;
for(int i=0;i<cnt;++i) {
for(int j=0;j<cnt;++j) {
for(int k=0;k<cnt;++k) {
C.mat[i][j]=(C.mat[i][j]+mat[i][k]*B.mat[k][j]%mod)%mod;
}
}
}
return C;
}
}M;
Mat powmul(Mat A,ll k) {
Mat B;
for(int i=0;i<cnt;++i) B.mat[i][i]=1;
while(k) {
if(k&1) B=B*A;
A=A*A;
k>>=1;
}
return B;
}
void init() {
mp['A']=0;mp['C']=1;
mp['G']=2;mp['T']=3;
}
char s[15];
int main() {
int m;ll n;
init();
while(~scanf("%d%I64d",&m,&n)) {
ac.init();id.clear();
for(int i=1;i<=m;++i) {
scanf("%s",s);
ac.insert(s);
}
ac.build();
cnt=0;//矩阵的大小
for(int i=0;i<ac.tot;++i) {
if(!ac.End[i]) id[i]=cnt++;
}
for(int i=0;i<ac.tot;++i) {
if(ac.End[i]) continue;
for(int k=0;k<4;++k) {
int j=ac.Next[i][k];
if(ac.End[j]) continue;
++M.mat[id[i]][id[j]];
}
}
Mat Ans=powmul(M,n);
ll ans=0;
for(int i=0;i<cnt;++i) {
ans=(ans+Ans.mat[0][i])%mod;
}
printf("%I64d\n",ans);
}
return 0;
}