208.10.10【JSOI2007】【BZOJ1030】【洛谷P4052】文字生成器(AC自動機)(DP)
阿新 • • 發佈:2018-12-14
洛谷傳送門
解析:
為什麼又是關鍵字啊啊啊啊!!!
思路:
不要試圖用容斥原理來做這道題。。。
這就是一個自動機上的
首先建出自動機,這時候我們得到所有串之間的包含關係。
然後,如果您有直接統計答案的做法,請私信博主,方便完善題解,這裡提供一種反向統計的方法。
我們不統計滿足條件的方案數,我們統計不滿足的方案數。
因為總方案十分好算,就是,所以滿足條件的方案數就是不滿足的補集。
那麼考慮如下: 其中是當前考慮的字首字串的長度,是所有能夠轉移到的自動機結點集合。
這個十分顯然,我們現在考慮怎麼處理不合法的方案。 考慮我們不能讓任何一個自動機中已經插入的字串出現在方案中,我們只需要記錄結點是否是某個字串的結尾。每次之前跳鏈檢驗一下就行了。
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
#define end End
cs int mod=10007;
cs int N=6005;
int son[N][26],dp[104][N],fail[N],end[N];
int tot=1,n,m;
inline void insert(char c[]){
int len=strlen(c),now=1;
for(int re i=0;i<len;++i){
int x=c[i]-'A';
if(!son[now][x])son[now][x]=++tot;
now=son[now][x];
}
end[ now]=true;
}
inline void build_ACauto(){
queue<int> q;
q.push(1);
while(!q.empty()){
int now=q.front();
q.pop();
for(int re i=0;i<26;++i){
if(son[now][i]){
int tmp=fail[now];
while(!son[tmp][i])tmp=fail[tmp];
fail[son[now][i]]=son[tmp][i];
q.push(son[now][i]);
}
}
}
}
inline int quickpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
char c[101];
signed main(){
for(int re i=0;i<26;++i)son[0][i]=1;
cin>>n>>m;
for(int re i=1;i<=n;++i){
scanf("%s",c);
insert(c);
}
build_ACauto();
dp[0][1]=1;
for(int re k=0;k<m;++k){
for(int re j=1;j<=tot;++j){
for(int re i=0;i<26;++i){
int now=j;
bool flag=true;
while(now){
if(end[son[now][i]]){
flag=false;
break;
}
now=fail[now];
}
if(!flag)continue;
now=j;
while(!son[now][i])now=fail[now];
now=son[now][i];
dp[k+1][now]+=dp[k][j];
dp[k+1][now]%=mod;
}
}
}
int ans=0;
for(int re i=1;i<=tot;++i)ans=(ans+dp[m][i])%mod;
cout<<(quickpow(26,m)-ans+mod)%mod;
return 0;
}