BZOJ1030 [JSOI2007]文本生成器[DP+AC自动机]
我学到现在才是初三学弟的水平。。哭
这里相当于求长度为$m$的,字符集$\{A...Z\}$的且不包含任一模式串的文本串个数。这是一个典型的AC自动机匹配计数问题。
设$f_{i,j}$表示在AC自动机上面走了$i$步在$j$点的方案数。
注意由于不能包含任一模式串,也就是说任意时刻都不能项后缀含有模式串的点上走,也就是fail树上所有以模式串结尾点为根的子树上的点。
这个可以在建立fail树的时候附带标记好。
于是转移的时候枚举字符集,下一步可以走向合法节点则转移之,否则不转移。边界$f_{0,0}=1$。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #define dbg(x) cerr << #x << " = " << x <<endl 8 using namespace std; 9 typedef long long ll; 10 typedef double db; 11 typedef pair<int,int> pii; 12 template<typename T>inline T _min(T A,T B){return A<B?A:B;} 13 template<typename T>inline T _max(T A,T B){return A>B?A:B;} 14 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;} 15 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;} 16 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;} 17 template<typename T>inline T read(T&x){ 18 x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1; 19 while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x; 20 } 21 const int P=10007,N=6000+7; 22 char s[100+7]; 23 int tr[N][26],nxt[N],ban[N],bin[N],tot,cnt; 24 int f[100+7][N]; 25 int n,m,ans; 26 inline void Insert(){ 27 int len=strlen(s+1),x=0; 28 for(register int i=1,c=s[1]-'A';i<=len;++i,c=s[i]-'A'){ 29 if(!tr[x][c])tr[x][c]=++tot; 30 x=tr[x][c]; 31 } 32 ban[x]=1; 33 } 34 queue<int> q; 35 inline void Build(){ 36 bin[cnt=1]=0; 37 for(register int i=0;i<26;++i)if(tr[0][i])nxt[tr[0][i]]=0,q.push(tr[0][i]); 38 while(!q.empty()){ 39 int x=q.front();q.pop(); 40 if(ban[nxt[x]])ban[x]=1; 41 if(!ban[x])bin[++cnt]=x; 42 for(register int i=0;i<26;++i){ 43 if(tr[x][i])nxt[tr[x][i]]=tr[nxt[x]][i],q.push(tr[x][i]); 44 else tr[x][i]=tr[nxt[x]][i]; 45 } 46 } 47 } 48 inline void add(int&A,int B){A+=B;A>=P&&(A-=P);} 49 inline int fpow(int x,int p){int ret=1;for(;p;p>>=1,x=x*x%P)if(p&1)ret=ret*x%P;return ret;} 50 inline void dp(){ 51 f[0][0]=1; 52 for(register int i=0;i<m;++i) 53 for(register int j=1;j<=cnt;++j)if(f[i][bin[j]]) 54 for(register int k=0;k<26;++k)if(!ban[tr[bin[j]][k]]) 55 add(f[i+1][tr[bin[j]][k]],f[i][bin[j]]); 56 } 57 58 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout); 59 read(n),read(m); 60 for(register int i=1;i<=n;++i)scanf("%s",s+1),Insert(); 61 Build(); 62 dp(); 63 for(register int i=1;i<=cnt;++i)add(ans,f[m][bin[i]]); 64 printf("%d\n",(fpow(26,m)-ans+P)%P); 65 return 0; 66 }
这里使用AC自动机位置描述状态,是为了有效满足合法性。注意。