Wireless Password HDU - 2825(AC自动机,状压DP)
题意:给m种子串,要求长度为n的构造串中至少有k种串,求方案数。
题解:将m个串放入字典树中,然后在字典树dfs搜索所有情况,搜索到底时要判断种类是否超过k个,这里用状态压缩存储,在加记忆化即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
#define ll long long
int n,m,ma[107][26],cnt,tk;
int fail[107];
int vis[107];
ll dp[107][26][1027];
int er[107];
const int mod=20090717;
string s;
void up(string s,int k){
int now=0;
for(int i=0;i<s.length();i++){
if(ma[now][s[i]-'a']==0){
ma[now][s[i]-'a']=++cnt;
}
now=ma[now][s[i]-'a'];
}
vis[now]|=er[k];
}
void fgo(){
queue<int>sa;
for(int i=0;i<=25;i++){
if(ma[0][i]){
sa.push(ma[0][i]);
fail[ma[0][i]]=0;
}
}
while(!sa.empty()){
int now=sa.front();
sa.pop();
for(int i=0;i<=25;i++){
if(ma[now][i]){
sa.push(ma[now][i]);
fail[ma[now][i]]=ma[fail[now]][i];
}
else{
ma[now][i]=ma[fail[now]][i];
}
}
vis[now]|=vis[fail[now]];
}
}
ll dfs(int p,int h,int k){
if(h==n){
int cnt=0;
for(int i=0;i<m;i++){
if(k&er[i])cnt++;
}
if(cnt>=tk){
return 1;
}
else{
return 0;
}
}
if(dp[p][h][k]!=-1){
return dp[p][h][k];
}
ll ans=0;
for(int i=0;i<=25;i++){
int now=k;
now|=vis[ma[p][i]];
ans+=dfs(ma[p][i],h+1,now);
ans%=mod;
}
return dp[p][h][k]=ans;
}
void init(){
memset(vis,0,sizeof(vis));
memset(fail,0,sizeof(fail));
memset(dp,-1,sizeof(dp));
memset(ma,0,sizeof(ma));
cnt=0;
}
int main(){
er[0]=1;
for(int i=1;i<=10;i++){
er[i]=er[i-1]*2;
}
while(1){
init();
scanf("%d%d%d",&n,&m,&tk);
if(n==0&&m==0&&tk==0)break;
for(int i=0;i<m;i++){
cin>>s;
up(s,i);
}
fgo();
printf("%lld
",dfs(0,0,0));
}
}