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的进阶版本,主题思想差不多 ...
随机推荐
- ASP.NET Newtonsoft 序列化反序列化
1.NuGet 安装 Newtonsoft 2.调用方法 一 NuGet 安装 Newtonsoft 二 调用方法 Student stu = new Student(); stu.ID = &quo ...
- I2C总线通信
UART 属于异步通信,比如电脑发送给单片机,电脑只负责把数据通过TXD 发送出来即可,接收数据是单片机自己的事情.而 I2C 属于同步通信, SCL 时钟线负责收发双方的时钟节拍, SDA 数据线负 ...
- kubernetes 里面pod时间修改
yaml文件中设置时区同步,只需要映射主机的“/etc/localtime”文件. apiVersion: extensions/v1beta1kind: Deploymentmetadata: na ...
- 现有各种SSTC电路图,欢迎补充,研究,开发
现有各种SSTC电路图,欢迎补充,研究,开发 496464505 2016-3-1 16:01:55 现在的各种SSTC基本都是这些图 2016-3-3 19:28:23 comter2001 ...
- SpringSecurity初步理解
Authenticating a User with LDAP 首先创建一个简单的web控制器 package hello; import org.springframework.web.bind.a ...
- vue 动态加载组建
<component :is="comp1"></component> data () { return { comp1:'', } } require.e ...
- Tensorflow实例:利用LSTM预测股票每日最高价(一)
RNN与LSTM 这一部分主要涉及循环神经网络的理论,讲的可能会比较简略. 什么是RNN RNN全称循环神经网络(Recurrent Neural Networks),是用来处理序列数据的.在传统的神 ...
- GitFlow原理浅析
一.Git优点 分布式存储 , 本地仓库包含了远程仓库的所有内容 . 安全性高 , 远程仓库文件丢失了也不怕 优秀的分支模型 , 创建/合并分支非常的方便 方便快速 , 由于代码本地都有存储 , 所以 ...
- SQLAlchemy模块的使用教程
数据库表是一个二维表,包含多行多列.把一个表的内容用Python的数据结构表示出来的话,可以用一个list表示多行,list的每一个元素是tuple,表示一行记录,比如,包含id和name的user表 ...
- Mvc_扩展@html
HtmlHelper的一个实例,它被定义在System.Web.Mvc名称空间下的WebViewPage类,即它对于所有MVC页面都可用)建立好扩展方法后: @Html.CreateGanderRad ...