bzoj 1030 [JSOI2007]文字生成器 (Trie圖+DP)
阿新 • • 發佈:2018-12-11
題目大意:給你一堆字串,一個串不合法的條件是這些字串中任意一個是這個串的子串,求合法的串的數量
其實這道題比 [HNOI2008]GT考試 那道題好寫一些,但道理是一樣的
只不過這道題的答案可以轉化為 所有可能的字串(26^m)數量 - 不合法的字串數量
定義f[i][j]表示匹配到了第i個字元,現在在Trie樹上匹配到了第j個節點的方案數
GT考試是跳Next,每次找出 和 插入這個字元後形成的字串 具有相同最長字尾的位置
那麼對於Trie圖來說,這不就是fail指標麼
Trie樹被補全成Trie樹後
如果在原來的Trie樹中某個節點x,它並沒有兒子ch[x][c],那麼在補全後,ch[x][c]自動指向的是x的fail指標指向的c兒子,即ch[fail[x]][c]
這不正是我們想要轉移的位置麼,非常智慧
總結:字串套KMP/AC自動機/Trie圖的題,通常都有f[i][j]表示文字串匹配到了第i位,模式串匹配到了第j位這類狀態,且有一些題可以用矩陣乘法優化。
#include <queue> #include <cmath> #include <cstdio> #include <cstring> #include <algorithm> #define ll long long #define N 6010 #define M 28 #define mod 10007 #define ui unsigned int #define idx(x) (x-'A'+1) #define inf 0x3f3f3f3f using namespace std; //re int n,m; char str[65][110]; int f[120][N]; int qpow(int x,int y){ int ans=1; while(y){ if(y&1) ans=(ans*x)%mod; x=(x*x)%mod,y>>=1; }return ans; } struct Trie{ int ch[N][M],fa[N],fail[N],ed[N],tot; void Build() { for(int i=1;i<=n;i++) { int len=strlen(str[i]+1),x=0; for(int j=1;j<=len;j++) { int c=idx(str[i][j]); if(!ch[x][c]) tot++,ch[x][c]=tot,fa[tot]=x; x=ch[x][c]; if(j==len) ed[x]=1; } } } void Fail() { queue<int>q; for(int i=1;i<=26;i++) if(ch[0][i]) q.push(ch[0][i]); while(!q.empty()) { int x=q.front();q.pop(); for(int i=1;i<=26;i++) if(ch[x][i]) fail[ch[x][i]]=ch[fail[x]][i], q.push(ch[x][i]); else ch[x][i]=ch[fail[x]][i]; } } int solve() { f[0][0]=1; queue<int>q; for(int i=0;i<=m;i++) for(int x=0;x<=tot;x++) for(int c=1;c<=26;c++) { int flag=1; for(int k=ch[x][c];k;k=fail[k]) if(ed[k]){flag=0;break;} if(!flag) continue; (f[i+1][ch[x][c]]+=f[i][x])%=mod; } int ans=0; for(int x=0;x<=tot;x++) if(!ed[x]) (ans+=f[m][x])%=mod; return ans; } }t; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",str[i]+1); t.Build(); t.Fail(); printf("%u\n",(qpow(26,m)-t.solve()+mod)%mod); return 0; }