【BZOJ1559】[JSOI2009]密码(AC自动机,动态规划,搜索)
【BZOJ1559】[JSOI2009]密码(AC自动机,动态规划,搜索)
题面
题解
首先求方案数显然是构建\(AC\)自动机之后再状压\(dp\),似乎没有什么好讲的。
现在考虑答案小于\(42\)的时候的怎么输出方案。
首先明白这样一点,如果一个位置可以不属于任何一个字符串而独立出来,那么它就可以贡献\(26\)种方案,再加之其它的字符串可以随意调换顺序,因此不可能有一个位置可以随意填放。所以这样的答案必定是所有\(n\)个字符串全部紧密的贴在一起形成的,直接\(O(n!)\)爆搜即可。
爆搜什么的懒得写了,就这样吧。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
char ch[20];
struct Node{int vis[26],fail,lt;}t[200];
int L,n,tot;ll f[26][105][1<<10],ans;
void insert(char *ch,int id)
{
int now=0,l=strlen(ch+1);
for(int i=1;i<=l;++i)
{
if(!t[now].vis[ch[i]-97])
t[now].vis[ch[i]-97]=++tot;
now=t[now].vis[ch[i]-97];
}
t[now].lt=1<<id;
}
void Build()
{
queue<int> Q;
for(int i=0;i<26;++i)
if(t[0].vis[i])Q.push(t[0].vis[i]);
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=0;i<26;++i)
if(t[u].vis[i])
t[t[u].vis[i]].fail=t[t[u].fail].vis[i],Q.push(t[u].vis[i]);
else t[u].vis[i]=t[t[u].fail].vis[i];
t[u].lt|=t[t[u].fail].lt;
}
}
int main()
{
L=read();n=read();
for(int i=0;i<n;++i)scanf("%s",ch+1),insert(ch,i);
Build();f[0][0][0]=1;
for(int i=1;i<=L;++i)
for(int j=0;j<=tot;++j)
for(int l=0;l<1<<n;++l)
if(f[i-1][j][l])
for(int k=0;k<26;++k)
f[i][t[j].vis[k]][l|t[t[j].vis[k]].lt]+=f[i-1][j][l];
for(int i=0;i<=tot;++i)ans+=f[L][i][(1<<n)-1];
printf("%lld\n",ans);
return 0;
}