bzoj 1030: [JSOI2007]文字生成器
阿新 • • 發佈:2018-12-28
AC自動機上跑DP
注意的細節是fail鏈上有標記 該節點也要標記(就好比 “ ********123******** ” 索然最後沒有出現123 但是中間出現了123 這種情況不可取!)
/************************************************************** Problem: 1030 User: lxy8584099 Language: C++ Result: Accepted Time:132 ms Memory:6044 kb ****************************************************************/ /* 有點像 1009 TG考試 不過這個資料挺小 不用矩陣加速 我們求出不合法的個數 用總數減去就行了 一個串的時候我們用KMP轉移 因為有多個串 所以建個AC自動機 轉移 之後的就和1009一模一樣了 f[i][j]表示 表示前i個字元 目前節點為j 的不符合方案數 轉移: f[i][ c[j][k] ] += f[i-1][j] (c[j][k]不是某個認識單詞的結尾) 自動機沒有兒子自動接上fail 方便!!! 考慮到如果某節點fail鏈跳到 0 的路上也遇到標記的話 這個點也是不可取的! 處理方法:我們在自動機上用或運算。*/ #include<queue> #include<cstdio> #include<cstring> using namespace std; const int N=1e4+50; const int MOD=10007; int c[N][26],last[N],n,m,tot; int f[105][N],ans=0,sum=1; bool vis[N]; void Add(char *s) { int l=strlen(s),u=0; for(int i=0;i<l;i++) { int v=s[i]-'A'; if(!c[u][v]) c[u][v]=++tot; u=c[u][v]; } vis[u]=1; } void Init() { scanf("%d%d",&n,&m);char s[105]; for(int i=1;i<=n;i++) scanf("%s",s),Add(s); } void Ac_build() { queue<int> q; for(int i=0;i<26;i++) if(c[0][i]) q.push(c[0][i]); while(!q.empty()) { int u=q.front();q.pop(); for(int v=0;v<26;v++) { if(c[u][v]) { last[c[u][v]]=c[last[u]][v]; vis[c[u][v]]|=vis[c[last[u]][v]]; // fial鏈有標記 該節點不能到達! q.push(c[u][v]);continue; } c[u][v]=c[last[u]][v]; // 這叫啥。。忘了。 } } } void Solve() { f[0][0]=1; for(int i=0;i<m;i++) for(int u=0;u<=tot;u++) for(int v=0;v<26;v++) if(!vis[c[u][v]]) (f[i+1][c[u][v]]+=f[i][u])%=MOD; for(int i=0;i<=tot;i++) (ans+=f[m][i])%=MOD; for(int i=1;i<=m;i++) (sum*=26)%=MOD; printf("%d\n",(sum-ans+MOD)%MOD); } int main() { Init(); Ac_build(); Solve(); return 0; }