题目描述

输入

输出

样例输入

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. BLDC

    BLDC working principle: https://www.bilibili.com/video/av31423350?from=search&seid=1569792618769 ...

  2. 免安装的Tomcat基本配置和安装

    大家都知道tomcat吧!因为Tomcat 技术先进.性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器,也是运行Servlet和JS ...

  3. 你真的了解volatile关键字吗?

    volatile关键字经常在并发编程中使用,其特性是保证可见性以及有序性,但是关于volatile的使用仍然要小心,这需要明白volatile关键字的特性及实现的原理,这也是本篇文章的主要内容. 一. ...

  4. FreeRTOS 任务与调度器(1)

    前言: Task.c和Task.h文件内是FreeRTOS的核心内容,所有任务和调度器相关的API函数都在这个文件中,它包括下图这些内容FreeRTOS文件如下: Task.c和Task.h文件内是F ...

  5. QQ聊天记录备份助手 v1.0——搜索、备份、恢复QQ聊天记录文件,重装系统必备

    项目地址 https://github.com/guoyaohua/QQBackUp 开发环境 Netbean JAVA 功能描述 自动搜索硬盘中的QQ聊天记录文件 可选择的备份QQ数据记录 还原QQ ...

  6. 写入mssql出现乱码

    1.出现乱码的场景如下: --------------------------------------------------------------------------------------- ...

  7. 分页查询信息(使用jdbc连接mysql数据库实现分页查询任务)

             分页查询信息       使用jdbc连接mysql数据库实现分页查询任务 通过mysql数据库提供的分页机制,实现商品信息的分页查询功能,将查询到的信息显示到jsp页面上. 本项目 ...

  8. 个人阅读作业 --软件工程M1/M2总结

    软件工程M1/M2总结 写在前面的话: 这学期的软件工程伴着考期的展开逐渐落下帷幕,回顾这学期的软件工程,我感觉我的热情在一次又一次的失落中逐步消耗殆尽,每个人对于这门课的体验都会有所不同吧,可以确定 ...

  9. 《Linux内核分析》课程第八周学习总结

    姓名:何伟钦 学号:20135223 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/U ...

  10. 20135337——linux实践三:ELF文件格式分析(32位系统)

    ELF文件格式分析 可重定位文件 十六进制形式显示内容 显示各个段.符号表相关信息 查看各个段信息 elf文件头信息 段表 符号表信息 查看堆栈 具体分析 1.ELF文件头信息(小字节优先,均十六进制 ...