BZOJ1559[JSOI2009]密码——AC自动机+DP+搜索
题目描述
输入
输出
样例输入
hello
world
样例输出
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+搜索的更多相关文章
- [BZOJ1559][JSOI2009]密码(AC自动机)
http://www.lydsy.com/JudgeOnline/problem.php?id=1559 2009年的省选题虽然比起现在简单了不少,但对我来说还是很有挑战性的. 首先对于这种多串匹配问 ...
- BZOJ 1559: [JSOI2009]密码( AC自动机 + 状压dp )
建AC自动机后, dp(x, y, s)表示当前长度为x, 在结点y, 包括的串的状态为s的方案数, 转移就在自动机上走就行了. 对于输出方案, 必定是由给出的串组成(因为<=42), 所以直接 ...
- [JSOI2009]密码 [AC自动机]
题面 bzoj luogu 首先看到这题就知道随便暴枚 只要是多项式算法都能过 先常规建AC自动机 注意被别的单词包含的单词没有存在的价值 剩余单词状压 大力dp f[长度][节点编号][状态] \( ...
- [BZOJ 1559] [JSOI2009] 密码 【AC自动机DP】
题目链接:BZOJ - 1559 题目分析 将给定的串建成AC自动机,然后在AC自动机上状压DP. 转移边就是Father -> Son 或 Now -> Fail. f[i][j][k] ...
- HDU 2457 DNA repair(AC自动机+DP)题解
题意:给你几个模式串,问你主串最少改几个字符能够使主串不包含模式串 思路:从昨天中午开始研究,研究到现在终于看懂了.既然是多模匹配,我们是要用到AC自动机的.我们把主串放到AC自动机上跑,并保证不出现 ...
- POJ1625 Censored!(AC自动机+DP)
题目问长度m不包含一些不文明单词的字符串有多少个. 依然是水水的AC自动机+DP..做完后发现居然和POJ2778是一道题,回过头来看都水水的... dp[i][j]表示长度i(在自动机转移i步)且后 ...
- HDU2296 Ring(AC自动机+DP)
题目是给几个带有价值的单词.而一个字符串的价值是 各单词在它里面出现次数*单词价值 的和,问长度不超过n的最大价值的字符串是什么? 依然是入门的AC自动机+DP题..不一样的是这题要输出具体方案,加个 ...
- HDU2457 DNA repair(AC自动机+DP)
题目一串DNA最少需要修改几个基因使其不包含一些致病DNA片段. 这道题应该是AC自动机+DP的入门题了,有POJ2778基础不难写出来. dp[i][j]表示原DNA前i位(在AC自动机上转移i步) ...
- hdu 4117 GRE Words AC自动机DP
题目:给出n个串,问最多能够选出多少个串,使得前面串是后面串的子串(按照输入顺序) 分析: 其实这题是这题SPOJ 7758. Growing Strings AC自动机DP的进阶版本,主题思想差不多 ...
随机推荐
- jdk_1_8_1
JAVA_HOME=/usr/local/java/jdk1.8.0_181 PATH=$JAVA_HOME/bin:$PATH JAVA_BINDIR=/usr/local/java/jdk1.8. ...
- 新页面,简单的tree视图写法
.xml文件 <?xml version="1.0"?><openerp> <data> <!--Tree view--> < ...
- nginx 安装问题
yum -y install xxx pcre-devel openssl-devel zlib-devel 这个三个包需要 有时候,我们需要单独安装nginx,来处理大量的下载请求.单独在 ...
- 通用漏洞评估方法CVSS3.0简表
CVSS3.0计算分值共有三种维度: 1. 基础度量. 分为 可利用性 及 影响度 两个子项,是漏洞评估的静态分值. 2. 时间度量. 基础维度之上结合受时间影响的三个动态分值,进而评估该漏洞的动态分 ...
- P4099 [HEOI2013]SAO
P4099 [HEOI2013]SAO 贼板子有意思的一个题---我()竟然没看题解 有一张连成树的有向图,球拓扑序数量. 树形dp,设\(f[i][j]\)表示\(i\)在子树中\(i\)拓扑序上排 ...
- Flutter - Json序列化
这个问题,FlutterChina小组已经说明的非常清楚易懂了. 详见https://flutterchina.club/json/
- Linux配置mail客户端发送邮件
1. 概述 在Linux操作系统环境中,可以配置邮件服务器,也可以配置邮箱客户端.本篇主要是配置邮件客户端,这对于发送服务器一些系统信息十分有必要. 2. mail客户端安装 2.1 安装mailx ...
- Dell BOSS 卡是什么
全名: Boot Optimized Storage Solution 针对 M.2 接口的 SSD,主板上必须设计接口进行适配. 设计一款主板对于硬件厂商来说是有成本的,其中包括 主板设计成本 产品 ...
- Centos下PPTP环境部署记录
PPTP(点到点隧道协议)是一种用于让远程用户拨号连接到本地的ISP,通过因特网安全远程访问公司资源的新型技术.它能将PPP(点到点协议)帧封装成IP数据包,以便能够在基于IP的互联网上进行传输.PP ...
- 完整部署CentOS7.2+OpenStack+kvm 云平台环境(2)--云硬盘等后续配置
继上一篇博客介绍了完整部署CentOS7.2+OpenStack+kvm 云平台环境(1)--基础环境搭建,本篇继续讲述后续部分的内容 1 虚拟机相关1.1 虚拟机位置介绍 openstack上创建的 ...