题意
给出n个字符串,求有多少个长度为L的字符串满足每个字符串出现至少一次。字符串仅由小写字母组成。
若方案书<=42的输出方案。
n<=10,L<=25,字符串长度<=10
分析
首先把重复和被包含的字符串去掉,建立AC自动机。
设f[i,j,k]表示在AC自动机上走了i步,走到第j个点,n个字符串的出现情况为k(k为n位二进制)的方案数。直接枚举转移即可。
输出方案数的话,注意到若字符串的某一个位不属于n个字符串中的任何一个,则方案数必然>=52,所以这必然是由n个字符串通过某种排列顺序紧凑排列后得到的。那么我们只要枚举字符串的排列顺序后按照字典序输出即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long LL;
int n,L,ch[105][26],num[105],fail[105],sz,bin[15],bor[15][15],g1,g[15],a1,len[15],rank[50],m;
queue<int> que;
char str[15][15],a[50][30];
LL f[2][105][1105];
bool vis[15],del[15];
int find(int x,int y)
{
for (int i=min(len[x],len[y]);i>=0;i--)
{
int flag=0;
for (int j=0;j<i;j++)
if (str[x][len[x]-i+j]!=str[y][j]) {flag=1;break;}
if (!flag) return i;
}
}
void ins(int x)
{
int now=0;
for (int i=0;i<len[x];i++)
if (!ch[now][str[x][i]-'a']) now=ch[now][str[x][i]-'a']=++sz;
else now=ch[now][str[x][i]-'a'];
num[now]=x;
}
void get_fail()
{
for (int i=0;i<26;i++) if (ch[0][i]) que.push(ch[0][i]);
while (!que.empty())
{
int u=que.front();que.pop();
for (int i=0;i<26;i++)
{
int v=ch[u][i],k=ch[fail[u]][i];
if (v) fail[v]=k,que.push(v);
else ch[u][i]=k;
}
}
}
void dfs(int x)
{
if (x>m)
{
a1++;
int l=0;
for (int i=1;i<=g1;i++)
for (int j=bor[g[i-1]][g[i]];j<len[g[i]];j++)
a[a1][++l]=str[g[i]][j];
if (l!=L) a1--;
return;
}
for (int i=1;i<=n;i++)
if (!del[i]&&!vis[i]) vis[i]=1,g[++g1]=i,dfs(x+1),vis[i]=0,g1--;
}
bool cmp(int x,int y)
{
for (int i=1;i<=L;i++)
if (a[x][i]<a[y][i]) return 1;
else if (a[x][i]>a[y][i]) return 0;
return 0;
}
void dp()
{
bin[0]=1;
for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2;
f[0][0][0]=1;
int now=0;
for (int i=0;i<L;i++)
{
now=1-now;
memset(f[now],0,sizeof(f[now]));
for (int j=0;j<=sz;j++)
for (int k=0;k<bin[n];k++)
if (f[now^1][j][k])
for (int l=0;l<26;l++)
{
int x=ch[j][l],y=k;
if (num[x]) y|=bin[num[x]-1];
f[now][x][y]+=f[now^1][j][k];
}
}
LL ans=0;
int s=0;
for (int i=1;i<=n;i++)
if (!del[i]) s+=bin[i-1];
for (int i=0;i<=sz;i++)
ans+=f[now][i][s];
printf("%lld\n",ans);
if (ans<=42)
{
dfs(1);
for (int i=1;i<=a1;i++) rank[i]=i;
sort(rank+1,rank+a1+1,cmp);
for (int i=1;i<=a1;i++)
{
for (int j=1;j<=L;j++)
putchar(a[rank[i]][j]);
puts("");
}
}
}
bool check(int x,int y)
{
for (int i=0;i<=len[y]-len[x];i++)
{
int j=0;
for (j=0;j<len[x];j++)
if (str[x][j]!=str[y][i+j]) break;
if (j==len[x]) return 1;
}
return 0;
}
int main()
{
scanf("%d%d",&L,&n);m=n;
for (int i=1;i<=n;i++) scanf("%s",str[i]),len[i]=strlen(str[i]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
bor[i][j]=find(i,j);
if (j>i&&!del[j]&&bor[i][j]==len[i]&&!del[i]) del[i]=1,m--;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (len[j]>len[i]&&!del[j]&&!del[i]&&check(i,j)) del[i]=1,m--;
for (int i=1;i<=n;i++)
if (!del[i]) ins(i);
get_fail();
dp();
return 0;
}