1. 程式人生 > >bzoj1030: [JSOI2007]文本生成器(AC自動機+DP)

bzoj1030: [JSOI2007]文本生成器(AC自動機+DP)

fail 當前 cst 分享圖片 ott 給定 -a 生成 tdi

  第一次寫這類題...懵

  直接計算答案不好計算,所以補集轉化求不合法的方案。

  首先考慮樸素的DP,設$f(i, s)$表示前$i$個字符,字符串為$s$的方案數,且任意一個給定串都不存在$s$中。

  我們知道在一個字符串裏找其他的字符串是AC自動機的強項,那麽我們就可以考慮在AC自動機上跑DP,每次$+j$都在AC自動機上匹配,如果匹配到單詞結尾的話就不能轉移,否則就是可以轉移的。

  所以設$f(i, j)$為前$i$個字符,當前匹配到AC自動機上第$j$個節點的方案數,如果沿著fail一直往上的所有節點都不是單詞結尾就可以轉移了。

  註意是大寫字母T_T

技術分享圖片
#include<iostream>
#include
<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define MOD(x) ((x)>=mod?(x)-mod:(x)) #define ll long long using namespace std; const int maxn=6010, maxm=110, mod=10007; struct poi{int nxt[26], fail;}tree[maxn*maxm]; int n, m, ans, tott; int f[maxm][maxn], h[maxn];
bool cnt[maxn]; char s[maxm]; inline void read(int &k) { int f=1; k=0; char c=getchar(); while(c<0 || c>9) c==-&&(f=-1), c=getchar(); while(c<=9 && c>=0) k=k*10+c-0, c=getchar(); k*=f; } inline void insert() { int len=strlen(s+1
), now=0; for(int i=1, ch;i<=len;i++) { if(!tree[now].nxt[ch=s[i]-A]) tree[now].nxt[ch]=++tott; now=tree[now].nxt[ch]; } cnt[now]=1; } inline void getfail() { int front=1, rear=0; tree[0].fail=-1; for(int i=0, too;i<26;i++) if((too=tree[0].nxt[i])) h[++rear]=too; while(front<=rear) { int now=h[front++]; for(int i=0, too;i<26;i++) if((too=tree[now].nxt[i])) tree[too].fail=tree[tree[now].fail].nxt[i], h[++rear]=too; else tree[now].nxt[i]=tree[tree[now].fail].nxt[i]; cnt[now]|=cnt[tree[now].fail]; } } int main() { read(n); read(m); for(int i=1;i<=n;i++) scanf("%s", s+1), insert(); getfail(); f[0][0]=1; for(int i=1;i<=m;i++) for(int j=0;j<=tott;j++) if(!cnt[j]) for(int k=0;k<26;k++) f[i][tree[j].nxt[k]]=MOD(f[i][tree[j].nxt[k]]+f[i-1][j]); for(int i=0;i<=tott;i++) if(!cnt[i]) ans=MOD(ans+f[m][i]); int tot=1; for(int i=1;i<=m;i++) tot=1ll*tot*26%mod; printf("%d\n", MOD(tot-ans+mod)); }
View Code

bzoj1030: [JSOI2007]文本生成器(AC自動機+DP)