[JSOI2009]密码

Posted by Dispwnl on February 21, 2019

题目

题目描述

众所周知,密码在信息领域起到了不可估量的作用。对于普通的登陆口令以,唯一的破解方法就是暴力破解——逐个尝试所有可能的字母组合,但这是一项很耗时又容易被发现的工作。所以,为了获取对方的登陆口令,在暴力破解密码之前,必须先做大量的准备工作。经过情报的搜集,现在得到了若干有用信息,形如:

           “我观察到,密码中含有字符串*。”

例如,对于一个10位的密码以及观察到的字符串hello与world,可能的密码组合为helloworld与worldhello;而对于6位的密码以及到的字符串good与day,可能的密码组合为gooday。

有了这些信息,就能够大大地减少尝试的次数了。请编一个程序,计算所有密码组合的可能。密码中仅可能包含a-z之间的小写字母。

输入输出格式

输入格式:

输入数据首先输入两个整数L,N,分别表示密码的长度与观察到子串的个数。

接下来N行,每行若干个字符,描述了每个观察到的字符串。

输出格式:

输出数据第一行为一个整数,代表了满足所有观察条件字符串的总数。

若这个数字小于等于42,则按字典顺序输出所有密码的可能情况,每行一个,否则,只输出满足所有观察条件字符串的总数即可。

输入输出样例

输入样例#1:

10 2
hello
world

输出样例#1:

2
helloworld
worldhello

说明

对于100%的数据,1<=L<=25,1<=N<=10,每个观察到的字符串长不超过10,并且保证输出结果小于2^63。

题解

首先先不管输出每种情况,建出$AC$自动机后,$f_{i,j,k}$表示前$i$位时状态为$j$已经出现过状态$k$(状态压缩)的给定字符串的方案数

最终的答案就是$\sum_{i=0}^{N}f_{L,i,(1«n)-1}$,$N$是$AC$自动机节点数

现在看不大于$42​$的答案怎么输出

因为如果一个位置是不确定的,最少能产生$52$种答案

所以答案就是$n$个字符串的排列再处理重叠的部分

爆搜套爆搜加一点剪枝就行了(并不)

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define LL long long
using namespace std;
const int N=105,M=(1<<10)+1;
struct p{
	char a[31];
}Ans[N];
int L,n,tot,_N,cnt;
int qu[N],A[N],D[N],Len[N],tax[N],Id[N];
int _D[N][N];
LL f[31][N][M];
char a[11][11];
bool use[N];
bool cmp(p a,p b)
{
	for(int i=1;i<=L;++i)
	  if(a.a[i]<b.a[i]) return 1;
	  else if(a.a[i]>b.a[i]) return 0;
	return 1;
}
bool look(int id,int x,int y)
{
	for(int i=id,j=1;i<=Len[x];++i,++j)
	  if(a[x][i]!=a[y][j]) return 0;
	return 1;
}
int look(int x,int y)
{
	if(_D[x][y]!=-1) return _D[x][y];
	for(int i=1;i<=Len[x];++i)
	  if(look(i,x,y)) return _D[x][y]=Len[x]-i+1;
	return 0;
}
void Dfs(int ans,int x=1,int d=_N-L)
{
	if(x==n)
	{
		if(d) return;
		++cnt;
		for(int i=1;i<=Len[A[1]];++i)
		  Ans[cnt].a[i]=a[A[1]][i];
		int _cnt=Len[A[1]];
		for(int i=2;i<=n;++i)
		  for(int j=D[i]+1;j<=Len[A[i]];++j)
		    Ans[cnt].a[++_cnt]=a[A[i]][j];
		return;
	}
	if(cnt==ans) return;
	for(int i=min(d,look(A[x],A[x+1]));i>=0;--i)
	  D[x+1]=i,Dfs(ans,x+1,d-i);
}
void dfs(int ans,int x=0)
{
	if(x==n) return void(Dfs(ans));
	if(cnt==ans) return;
	for(int i=0;i<n;++i)
	  if(!use[i]) use[i]=1,A[x+1]=i,dfs(ans,x+1),use[i]=0;
}
struct AC{
	int fail[N],val[N];
	int son[N][26];
	void ins(int _L,int id)
	{
		int x=0;
		for(int i=1;i<=_L;++i)
		  if(son[x][a[id][i]-'a']) x=son[x][a[id][i]-'a'];
		  else son[x][a[id][i]-'a']=++tot,x=tot;
		val[x]=1<<id,_N+=_L,Len[id]=_L;
	}
	void GET_FAIL()
	{
		int hl=1,tl=0;
		for(int i=0;i<26;++i)
		  if(son[0][i]) qu[++tl]=son[0][i];
		while(hl<=tl)
		{
			int tt=qu[hl++];
			for(int i=0;i<26;++i)
			  if(son[tt][i]) fail[son[tt][i]]=son[fail[tt]][i],qu[++tl]=son[tt][i];
			  else son[tt][i]=son[fail[tt]][i];
			val[tt]|=val[fail[tt]];
		}
	}
	void Solve()
	{
		GET_FAIL(),f[0][0][0]=1;
		for(int k=1;k<=L;++k)
		  for(int i=0;i<=tot;++i)
			for(int j=0;j<26;++j)
			  for(int h=0;h<(1<<n);++h)
			    f[k][son[i][j]][h|val[son[i][j]]]+=f[k-1][i][h];
		LL ans=0;
		for(int i=0;i<=tot;++i)
		  ans+=f[L][i][(1<<n)-1];
		printf("%lld\n",ans);
		if(ans<=42)
		{
			memset(_D,-1,sizeof(_D));
			dfs(ans),sort(Ans+1,Ans+1+cnt,cmp);
			for(int i=1;i<=cnt;++i,printf("\n"))
			  for(int j=1;j<=L;++j)
			    printf("%c",Ans[i].a[j]);
		}
	}
}_A;
int main()
{
	scanf("%d%d",&L,&n);
	for(int i=0,_n;i<n;++i)
	  scanf("%s",a[i]+1),_A.ins(strlen(a[i]+1),i);
	return _A.Solve(),0;
}