【學術篇】SPOJ GEN Text Generator AC自動機+矩陣快速冪
阿新 • • 發佈:2018-12-31
還有5天省選才開始點字串這棵技能樹是不是太晚了點...
AC自動機不想講了QAQ.其實很久以前是學過然後打過板子的, 但也僅限於打過板子了~
之前莫名其妙學了一個指標版的但是好像不能用迴圈遍歷fail好像就啥也幹不了於是改成了陣列...)
其實就是Trie樹上掛fail指標... 然後可以完成多串的kmp的樣子...
直接看題吧. 題目大意: 求長度為\(L\)的,包含給定的\(n\)個短串中的至少一個的字串的數量.
考慮補集轉化, 考慮不含這些短串的字串的數量. 然後用所有長度為\(L\)的字串(\(26^L\))的數量減掉就行了.
然後建立AC自動機, 對於不存在的節點我們就直接讓它指向根就可以了.
令\(f[i][j]\)
然後根據臨接矩陣的特點, 我們求出\(f^L[i][j]\)就是從\(i\)開始走\(L\)步到\(j\)時合法的方案數.
然後我們最後要求的數量就是\(ans=\sum_{i=1}^tf[root][i]\), 其中\(t\)表示Trie樹上的節點數, 最後我們用\(26^L-ans\)即可.
有一個優化就是如果一個點的fail指標指向了一個有isend標記的節點, 那這個點也是不可達的(由fail指標的定義顯然), 我們就也把它貼上isend標記即可.
下面就是隆重登場的1A程式碼:
其實我的spoj賬號因為之前測試自動提交指令碼的緣故已經慘不忍睹了..然而一大部分都已經被我disqualify掉了2333~
#include <queue> #include <cstdio> #include <cstring> using namespace std; const int N=66; const int p=10007; char c[10]; int ch[N][26],fail[N],tot; bool isend[N]; struct mat{int ma[N][N];}a; inline void init(){ memset(ch,0,sizeof ch); tot=0; memset(fail,0,sizeof fail); memset(isend,0,sizeof isend); memset(a.ma,0,sizeof a.ma); } inline void inser(char* s){ int len=strlen(s),now=0; for(int i=0;i<len;++i){ int k=s[i]-'A'; if(!ch[now][k]) ch[now][k]=++tot; now=ch[now][k]; } isend[now]=1; } inline void make_fail(){ queue<int> q; for(int i=0;i<26;++i) if(ch[0][i]) q.push(ch[0][i]),fail[ch[0][i]]=0; while(!q.empty()){ int now=q.front(); q.pop(); if(isend[fail[now]]) isend[now]=1; for(int i=0;i<26;++i){ if(ch[now][i]) fail[ch[now][i]]=ch[fail[now]][i],q.push(ch[now][i]); else ch[now][i]=ch[fail[now]][i]; } } } inline void make_mat(){ for(int i=0;i<=tot;++i) for(int j=0;j<26;++j) if(!isend[ch[i][j]]) ++a.ma[i][ch[i][j]]; } mat mat_mul(mat a,mat b){ mat c; int ans; for(int i=0;i<=tot;++i) for(int j=0;j<=tot;++j){ ans=0; for(int k=0;k<=tot;++k){ ans+=a.ma[i][k]*b.ma[k][j]; while(ans>=p) ans-=p; } c.ma[i][j]=ans; } return c; } inline mat mat_pow(mat a,int b){ mat s=a; for(--b;b;b>>=1,a=mat_mul(a,a)) if(b&1) s=mat_mul(s,a); return s; } inline int qpow(int a,int b,int s=1){ for(a%=p;b;b>>=1,a=a*a%p) if(b&1) s=s*a%p; return s; } int main(){ int n,l; while(scanf("%d%d",&n,&l)!=EOF){ init(); for(int i=0;i<n;++i){ scanf("%s",c); inser(c); } make_fail(); make_mat(); mat b=mat_pow(a,l); int ans=qpow(26,l); for(int i=0;i<tot;++i){ ans-=b.ma[0][i]; if(ans<0) ans=ans+p; } printf("%d\n",ans%p); } }