题目描述

输入

输出

样例输入

10 2
hello
world

样例输出

2
helloworld
worldhello

提示

这题算是一个套路题了,多个串求都包含它们的长为L的串的方案数。

显然是一个在AC自动机(trie图)上DP,常规DP状态是f[i][j]表示在AC自动机上走了i步到达了j节点的方案数。

但这道题还要求包含所有模式串,而且模式串最多10个,因此再加一维f[i][j][k]表示在AC自动机上走了i步到达了j节点,已经包含的字符串状态为k的方案数,其中k是一个二进制状态。

但我们发现如果一个串x是另一个串y的子串,那么只要包含y就一定包含x,因此在DP之前还要去掉被包含的串。

我去掉被包含串的方法是当一个终止节点有子节点(在找fail指针之前)或者一个终止节点被其他点通过fail指针指向(在找fail指针之后),那么说明这个串被包含,就将他的终止标记删掉。

剩下还有输出方案,因为只在方案数<=42时输出,所以方案一定是由模式串组成并且相邻模式串首尾重复部分一定要去重。

为什么?

因为假如有一个随机字符,只有一个模式串,那么他们的方案数就是2*26=52>42,所以一定不包含随机字符。

而如果不将相邻模式串去重就能到达长度为L,那么去重之后就会出现随机字符,方案数还是会超过42。

综上所述,密码串就是由所有模式串(不包括是其他串子串的串)的排列组成,最多就10个串,预处理出任意两个模式串的重叠长度,爆搜一下就好了。

#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int s[120][30];
int fail[120];
int num[120];
long long f[3][120][1025];
int n,L,m;
int cnt;
char ch[30][30];;
int vis[120];
long long ans;
char res[50][30];
int lk[30][30];
int q[30];
int tot;
int v[30];
int rank[30];
int que[30];
void build(char *ch,int k)
{
int len=strlen(ch);
int now=0;
for(int i=0;i<len;i++)
{
int x=ch[i]-'a';
if(!s[now][x])
{
s[now][x]=++cnt;
}
now=s[now][x];
}
vis[now]=k;
}
void get_fail()
{
queue<int>q;
for(int i=0;i<26;i++)
{
if(s[0][i])
{
q.push(s[0][i]);
fail[s[0][i]]=0;
}
}
while(!q.empty())
{
int now=q.front();
q.pop();
for(int i=0;i<26;i++)
{
if(s[now][i])
{
fail[s[now][i]]=s[fail[now]][i];
q.push(s[now][i]);
}
else
{
s[now][i]=s[fail[now]][i];
}
}
}
}
void find_end()
{
for(int i=1;i<=cnt;i++)
{
if(vis[i])
{
for(int j=0;j<26;j++)
{
if(s[i][j])
{
vis[i]=0;
break;
}
}
}
}
get_fail();
for(int i=1;i<=cnt;i++)
{
if(vis[fail[i]])
{
vis[fail[i]]=0;
}
}
for(int i=1;i<=cnt;i++)
{
if(vis[i])
{
m++;
q[m]=vis[i];
num[i]=1<<(m-1);
}
}
}
void dp()
{
f[0][0][0]=1;
for(int i=0;i<L;i++)
{
memset(f[(i+1)&1],0,sizeof(f[(i+1)&1]));
for(int j=0;j<=cnt;j++)
{
for(int k=0;k<=(1<<m)-1;k++)
{
if(f[i&1][j][k])
{
for(int l=0;l<26;l++)
{
int x=s[j][l];
f[(i+1)&1][x][k|num[x]]+=f[i&1][j][k];
}
}
}
}
}
for(int i=0;i<=cnt;i++)
{
ans+=f[L&1][i][(1<<m)-1];
}
}
int get_lk(int x,int y)
{
int i,j;
bool flag;
int lx=strlen(ch[x]);
int ly=strlen(ch[y]);
for(i=min(lx,ly);i>0;i--)
{
flag=1;
for(j=0;j<i;j++)
{
if(ch[x][lx-i+j]!=ch[y][j])
{
flag=0;
break;
}
}
if(flag)
{
break;
}
}
return i;
}
void dfs(int dep)
{
if(dep>m)
{
tot++;
int l=0;
for(int i=1;i<dep;i++)
{
int len=strlen(ch[que[i]]);
for(int j=lk[que[i-1]][que[i]];j<len;j++)
{
res[tot][l]=ch[que[i]][j];
l++;
}
}
if(l!=L)
{
tot--;
}
return ;
}
for(int i=1;i<=m;i++)
{
if(!v[i])
{
v[i]=1;
que[dep]=q[i];
dfs(dep+1);
v[i]=0;
}
}
}
int cmp(int x,int y)
{
for(int i=0;i<L;i++)
{
if(res[x][i]!=res[y][i])
{
return res[x][i]<res[y][i];
}
}
return 0;
}
int main()
{
scanf("%d%d",&L,&n);
for(int i=1;i<=n;i++)
{
scanf("%s",ch[i]);
build(ch[i],i);
}
find_end();
dp();
printf("%lld\n",ans);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=m;j++)
{
lk[q[i]][q[j]]=get_lk(q[i],q[j]);
}
}
if(ans<=42)
{
dfs(1);
for(int i=1;i<=tot;i++)
{
rank[i]=i;
}
sort(rank+1,rank+tot+1,cmp);
for(int i=1;i<=tot;i++)
{
for(int j=0;j<L;j++)
{
printf("%c",res[rank[i]][j]);
}
printf("\n");
}
}
}

BZOJ1559[JSOI2009]密码——AC自动机+DP+搜索的更多相关文章

  1. [BZOJ1559][JSOI2009]密码(AC自动机)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1559 2009年的省选题虽然比起现在简单了不少,但对我来说还是很有挑战性的. 首先对于这种多串匹配问 ...

  2. BZOJ 1559: [JSOI2009]密码( AC自动机 + 状压dp )

    建AC自动机后, dp(x, y, s)表示当前长度为x, 在结点y, 包括的串的状态为s的方案数, 转移就在自动机上走就行了. 对于输出方案, 必定是由给出的串组成(因为<=42), 所以直接 ...

  3. [JSOI2009]密码 [AC自动机]

    题面 bzoj luogu 首先看到这题就知道随便暴枚 只要是多项式算法都能过 先常规建AC自动机 注意被别的单词包含的单词没有存在的价值 剩余单词状压 大力dp f[长度][节点编号][状态] \( ...

  4. [BZOJ 1559] [JSOI2009] 密码 【AC自动机DP】

    题目链接:BZOJ - 1559 题目分析 将给定的串建成AC自动机,然后在AC自动机上状压DP. 转移边就是Father -> Son 或 Now -> Fail. f[i][j][k] ...

  5. HDU 2457 DNA repair(AC自动机+DP)题解

    题意:给你几个模式串,问你主串最少改几个字符能够使主串不包含模式串 思路:从昨天中午开始研究,研究到现在终于看懂了.既然是多模匹配,我们是要用到AC自动机的.我们把主串放到AC自动机上跑,并保证不出现 ...

  6. POJ1625 Censored!(AC自动机+DP)

    题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后 ...

  7. HDU2296 Ring(AC自动机+DP)

    题目是给几个带有价值的单词.而一个字符串的价值是 各单词在它里面出现次数*单词价值 的和,问长度不超过n的最大价值的字符串是什么? 依然是入门的AC自动机+DP题..不一样的是这题要输出具体方案,加个 ...

  8. HDU2457 DNA repair(AC自动机+DP)

    题目一串DNA最少需要修改几个基因使其不包含一些致病DNA片段. 这道题应该是AC自动机+DP的入门题了,有POJ2778基础不难写出来. dp[i][j]表示原DNA前i位(在AC自动机上转移i步) ...

  9. hdu 4117 GRE Words AC自动机DP

    题目:给出n个串,问最多能够选出多少个串,使得前面串是后面串的子串(按照输入顺序) 分析: 其实这题是这题SPOJ 7758. Growing Strings AC自动机DP的进阶版本,主题思想差不多 ...

随机推荐

  1. java 二维数组和对象数组

    1.二维数组:二维数组就是存储一维数组(内存地址/引用)的数组 2.二维数组的初始化 1) int intA[][]={{1,2},{2,3},{3,4,5}}; 2) int [][] intB=n ...

  2. Python崛起:“人生苦短,我用Python”并非一句戏言

      这些年,编程语言的发展进程很快,在商业公司.开源社区两股力量的共同推动下,涌现出诸如Go.Swift这类后起之秀,其中最为耀眼的是Python.   在这里还是要推荐下我自己建的Python开发学 ...

  3. UVA1626 - Brackets sequence(区间DP--括号匹配+递归打印)

    题目描写叙述: 定义合法的括号序列例如以下: 1 空序列是一个合法的序列 2 假设S是合法的序列.则(S)和[S]也是合法的序列 3 假设A和B是合法的序列.则AB也是合法的序列 比如:以下的都是合法 ...

  4. Codeforces round 1083

    Div1 526 这个E考试的时候没调出来真的是耻辱.jpg A 求个直径就完事 #include<cstdio> #include<algorithm> #include&l ...

  5. LOJ558 我们的 CPU 遭到攻击 LCT

    传送门 题意:写一个数据结构,支持森林上:连边.删边.翻转点的颜色(黑白).查询以某一点为根的某棵树上所有黑色点到根的距离和.$\text{点数} \leq 10^5 , \text{操作数} \le ...

  6. [Oracle]构筑TDE 环境的例子

    构筑TDE 环境的例子: 测试环境:12.1.0.2 $ cd $ORACLE_HOME/network/admin$ vim sqlnet.ora $ pwd/u01/app/oracle/prod ...

  7. ul ol li的序号编号样式

    序号样式例子,下面是html代码(做参考) <ol> <li>列表内容列表内容列表内容列表</li> <li>列表内容列表内容列表内容列表</li ...

  8. IDEA启动服务阻塞,断点过程十分慢的问题

    使用debug无法启动项目但是使用run就可以启动程序,而且启动比以前的debug模式快的多 原因: 启动不了的原因是在项目中的方法上打了断点,导致项目无法继续编译 取消方法断点就可以了 在idea官 ...

  9. D. Mysterious Crime

    链接 [http://codeforces.com/contest/1043/problem/D] 题意 给你一个m*n的矩阵(m<=10,n<=1e5), 每一行的数字是1到n里不同的数 ...

  10. 《Linux内核设计与实现》课本第四章学习总结

    进程调度 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务系统分为两种: 抢占式多任务:Linux提供了抢占式的多任务模式,由调度程序来决定什么时候停止一个进程的运行 ...