题意:有m个模式串,问长度为n包含至少k中模式串(可重叠)的串有多少种,答案取模。
思路:状压dp,先说下dp数组含义,dp[i][j][k]代表长度为i的字符串状态为j,含有k种模式串。
由于可以重叠,直接AC自动机构造nxt数组后(同时操作模式串包含其他为其后缀的模式串个数),dp是分n次添加字符,每次枚举Trie树上的节点,枚举状态,枚举添加字符,根据合法状态推向合法状态的状压dp,枚举所添加字符后的操作,因为是可重叠,爬数即可。最后统计所有长度为n,含有模式串种类数>k的状态数量即可。
下面上代码
#include<cstdio>
#include<cstring>
#include<queue>
#define ll long long
using namespace std;
const int mod = 20090717;
const int maxn = 110,N=26;
int dp[N][110][1<<10],num[1<<10];
struct Trie{
int nxt[maxn][N],fail[maxn],end[maxn];
int root,tot;
int newnode(){
for(int i=0;i<N;i++)
nxt[tot][i]=-1;
end[tot++]=0;
return tot-1;
}
void init(){
tot=0;
root=newnode();
}
void insert(char buf[],int id){
int len=strlen(buf);
int now=root;
for(int i=0;i<len;i++){
int k=buf[i]-'a';
if(nxt[now][k]==-1)
nxt[now][k]=newnode();
now=nxt[now][k];
}
end[now]|=(1<<id);
}
void build(){
queue<int>que;
fail[root]=root;
for(int i=0;i<N;i++)
if(nxt[root][i]==-1) nxt[root][i]=root;
else{
fail[nxt[root][i]]=root;
que.push(nxt[root][i]);
}
while(!que.empty()){
int now=que.front();
que.pop();
end[now]|=end[fail[now]];
for(int i=0;i<N;i++){
if(nxt[now][i]==-1) nxt[now][i]=nxt[fail[now]][i];
else{
fail[nxt[now][i]]=nxt[fail[now]][i];
que.push(nxt[now][i]);
}
}
}
}
int solve(int n,int m,int k){
for(int i=0;i<=n;i++)
for(int j=0;j<=tot;j++)
for(int q=0;q<=(1<<m);q++)
dp[i][j][q]=0;
dp[0][0][0]=1;
for(int i=0;i<n;i++)
//枚举字符串长度为i,其实就是分n次,每次填上一个字母的方式拓展状态
for(int j=0;j<tot;j++)//枚举Trie树上的节点
for(int q=0;q<(1<<m);q++)//枚举一种状态
if(dp[i][j][q]>0){
//如果存在即可由此推其他状态,另存在就说明合法(我们只由合法推向合法)
for(int x=0;x<N;x++){//在此字符串后添加一个字母
int newi=i+1;
int newj=nxt[j][x];//因为可以重叠,所以我们爬树即可
int newq=(q|end[newj]);
//如果连重叠都没有可能性存在该字符串则newj回到了根节点,end[newj]则为0
//即使片尾可能不是完整字符串,这里面也只存着那些完整字符串的值,只要满足[k]即可
dp[newi][newj][newq]+=dp[i][j][q];
dp[newi][newj][newq]%=mod;
}
}
int ans=0;
for(int p=0;p<(1<<m);p++){
if(num[p]<k) continue;//包含字符串小于k种不计
for(int i=0;i<tot;i++){
ans=(ans+dp[n][i][p])%mod;
}
}
return ans;
}
};
char buf[20];
Trie ac;
int main()
{
int n,m,k;
for(int i=0;i<(1<<10);i++){
num[i]=0;
for(int j=0;j<10;j++)
if(i&(1<<j)) num[i]++;
}
while(scanf("%d%d%d",&n,&m,&k),n||m||k){
ac.init();
for(int i=0;i<m;i++){
scanf("%s",buf);
ac.insert(buf,i);
}
ac.build();
printf("%d\n",ac.solve(n,m,k));
}
}
好吧这确实是kuangbin的代码,我只是加了些注释。
类型标记转载好了,希望不会被喷。