题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1559

分析:

  这个题意真的是很**啊!!!直接说每一个字符串至少出现一次不就好了吗......一开始理解错了ORZ

  观察发现这个东西是字符串相关,并且有多个模板串,所有串的长度短并且串的数量不多,最多10个,因此大概可以想到一个AC自动机上面的状压。

  首先把被包含的单词去掉,它们对决策不影响,这样在写方程的时候就可以不考虑last了。

  令f(i,l,s)表示当位于AC自动机的状态i时,已经生成了l个字符,所有单词的出现情况为s的方案数。

  f(i,l,s) = sum{ f(j,l-1,s) | j->i } + sum{ f(j,l-1,s-{i}) | j->i },s包含单词i。刷表实现即可。

  ans=sum{ f(i,L,all) | 0<=i<=np }

  当答案小于等于42的时候直接在状态转移图上面倒着搜就可以了,看那些状态对当前状态有贡献,一路搜下去,最后把所有串排个序即可。

  时间复杂度O(L*N*len*2^N),最后的方案搜索因为方案很少几乎不要时间。

  get套路之:字符串算法构造状态辅助字符串有关dp!

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
typedef long long LL; int L,N,cnt,len;
char S[][],path[];
LL f[][][];
bool vis[];
struct mstring{
static const int maxn=;
char str[maxn];
mstring(){ memset(str,,sizeof(str)); }
friend bool operator < (mstring a,mstring b){
int n=min(strlen(a.str),strlen(b.str));
for(int i=;i<n;i++)
if(a.str[i]!=b.str[i]) return a.str[i]<b.str[i];
return strlen(a.str)<strlen(b.str);
}
}ss[];
struct Aho_Corasick_Automaton{
static const int maxn=;
int np,to[maxn][],val[maxn],fail[maxn],id[maxn];
Aho_Corasick_Automaton(){
np=; memset(to[],,sizeof(to[]));
}
void ins(char *s,int ii){
int p=,n=strlen(s);
for(int i=;i<n;i++){
int w=s[i]-'a';
if(!to[p][w]){
to[p][w]=++np,val[np]=id[np]=fail[np]=;
memset(to[np],,sizeof(to[np]));
}
p=to[p][w];
}
val[p]=,id[p]=ii;
}
void getfail(){
queue<int>q;
fail[]=;
for(int w=;w<;w++)
if(to[][w]) fail[to[][w]]=,q.push(to[][w]);
while(!q.empty()){
int p=q.front(); q.pop();
for(int w=;w<;w++){
int j=to[p][w];
if(!j){ to[p][w]=to[fail[p]][w]; continue; }
q.push(j);
fail[j]=to[fail[p]][w];
}
}
}
}ac; void data_in()
{
scanf("%d%d",&L,&N);
for(int i=;i<N;i++) scanf("%s",S[i]);
for(int i=;i<N;i++) if(!vis[i]){
int n=strlen(S[i]);
for(int j=;j<N;j++) if(i!=j){
int m=strlen(S[j]);
if(m>n) continue;
bool ok;
for(int k=;k<=n-m;k++){
ok=;
for(int l=;l<m;l++)
if(S[i][k+l]!=S[j][l]){ ok=; break; }
if(ok) break;
}
if(ok) vis[j]=;
}
}
int tmp=;
for(int i=;i<N;i++) if(!vis[i])
memcpy(S[tmp++],S[i],sizeof(S[i]));
N=tmp;
}
void run(int i,int l,int s)
{
if(i==&&l==&&s==){
cnt++;
for(int j=len-;j>=;j--)
ss[cnt].str[len--j]=path[j];
ss[cnt].str[len]='\0';
return;
}
for(int j=;j<=ac.np;j++) if(f[j][l-][s])
for(int w=;w<;w++) if(ac.to[j][w]==i){
path[len++]=w+'a'; run(j,l-,s);
len--; break;
}
if(ac.val[i]){
s^=<<ac.id[i];
for(int j=;j<=ac.np;j++) if(f[j][l-][s])
for(int w=;w<;w++) if(ac.to[j][w]==i){
path[len++]=w+'a'; run(j,l-,s);
len--; break;
}
}
}
void work()
{
for(int i=;i<N;i++) ac.ins(S[i],i);
ac.getfail();
f[][][]=;
int all=(<<N)-;
for(int l=;l<L;l++)
for(int i=;i<=ac.np;i++){
int ori=ac.val[i]?<<ac.id[i]:;
for(int s=ori;s<=all;s=(s+)|ori){
if(!f[i][l][s]) continue;
for(int w=;w<;w++){
int j=ac.to[i][w];
if(ac.val[j]) f[j][l+][s|(<<ac.id[j])]+=f[i][l][s];
else f[j][l+][s]+=f[i][l][s];
}
}
}
LL ans=;
for(int i=;i<=ac.np;i++) ans+=f[i][L][all];
cout<<ans<<'\n';
if(ans<=){
for(int i=;i<=ac.np;i++)
if(f[i][L][all]) run(i,L,all);
sort(ss+,ss+ans+);
for(int i=;i<=ans;i++) puts(ss[i].str);
}
}
int main()
{
data_in();
work();
return ;
}

BZOJ 1559 JSOI2009 密码 状压dp+AC自动机+搜索的更多相关文章

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

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

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

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

  3. BZOJ 1087 题解【状压DP】

    1087: [SCOI2005]互不侵犯King Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 3112  Solved: 1816[Submit][ ...

  4. BZOJ 4000: [TJOI2015]棋盘( 状压dp + 矩阵快速幂 )

    状压dp, 然后转移都是一样的, 矩阵乘法+快速幂就行啦. O(logN*2^(3m)) ------------------------------------------------------- ...

  5. BZOJ 4057: [Cerc2012]Kingdoms( 状压dp )

    状压dp.... 我已开始用递归结果就 TLE 了... 不科学啊...我dp基本上都是用递归的..我只好改成递推 , 刷表法 将全部公司用二进制表示 , 压成一个数 . 0 表示破产 , 1 表示没 ...

  6. BZOJ 2073: [POI2004]PRZ( 状压dp )

    早上这道题没调完就去玩NOI网络同步赛了.... 状压dp , dp( s ) 表示 s 状态下所用的最短时间 , 转移就直接暴力枚举子集 . 可以先预处理出每个状态下的重量和时间的信息 . 复杂度是 ...

  7. bzoj 2669 题解(状压dp+搜索+容斥原理)

    这题太难了...看了30篇题解才整明白到底咋回事... 核心思想:状压dp+搜索+容斥 首先我们分析一下,对于一个4*7的棋盘,低点的个数至多只有8个(可以数一数) 这样的话,我们可以进行一个状压,把 ...

  8. BZOJ 2004 公交线路(状压DP+矩阵快速幂)

    注意到每个路线相邻车站的距离不超过K,也就是说我们可以对连续K个车站的状态进行状压. 然后状压DP一下,用矩阵快速幂加速运算即可. #include <stdio.h> #include ...

  9. BZOJ 1226 学校食堂(状压DP)

    状压DP f(i,j,k)表示前i−1个人已经吃了饭,且在i之后的状态为j的人也吃了饭(用二进制表示后面的状态),最后吃的那个人是i之后的第k个 (注意k可以是负数) 然后 如果j&1=1那么 ...

随机推荐

  1. Web | JavaScript的引用数据类型强制转换类型

    我在这里主要的想提下的是JavaScript中的引用类型进行强制转换类型.因为对于基本数据类型的变换大多都是雷同的,很容易熟知,但是引用数据类型有一点小插曲. JavaScript的引用类型主要为对象 ...

  2. C++程序设计入门 引用和动态内存管理学习

    引用: 引用就是另一个变量的别名,通过引用所做的读写操作实际上是作用于原变量上. 由于引用是绑定在一个对象上的,所以定义引用的时候必须初始化. 函数参数:引用传递 1.引用可做函数参数,但调用时只需 ...

  3. 【CSU 1803】2016 (数学)

    Description 给出正整数 n 和 m,统计满足以下条件的正整数对 (a,b) 的数量: 1. 1≤a≤n,1≤b≤m; 2. a×b 是 2016 的倍数. Input 输入包含不超过 30 ...

  4. vue-cli3 创建选项选择

    1.创建新项目: vue create hello-world 2.选择配置 3.自定义选择配置,需要什么就选什么 4. 是否使用带历史纪录的路由,这里一般是Y 5.预编译器选择什么 6.eslint ...

  5. Redis 之江湖遇险-复制运维及优化

    一. 前言 上一篇Redis 之深入江湖-复制原理中说了复制的原理,那么在理解复制原理之后,还要知道在这复制功能的背后,还有哪些坑要注意一下,毕竟坑是要跳过去的,而不是跳进去的. 二. 读写分离的一些 ...

  6. Python - 入门基础(一)

    1.解释器路径 #!/usr/bin/env python 2.编码 # -*- coding:utf8 -*- 1.ascill ---00000000  (8个位表示) 缺点:表示不了英文 2.u ...

  7. Hadoop分布式集群搭建_1

    Hadoop是一个开源的分布式系统框架 一.集群准备 1. 三台虚拟机,操作系统Centos7,三台主机名分别为k1,k2,k3,NAT模式 2.节点分布 k1: NameNode DataNode ...

  8. Python学习手册之Python介绍、基本语法(一)

    一.什么是python? python是一种高级的编程语言.它适合编写一些应用程序,比如:网站编程,脚本编程,科学计算和最近非常热门的AI(人工智能).目前,Google,腾讯,百度,阿里巴巴,豆瓣都 ...

  9. HxUtils: 批量转换换行符,print2to3

    在 windows 和 linux 系统,换行符有时需要转换,其代码文件 HxUntils.py 如下: ''' HxUtils.py 2018 by x01 ''' import os, sys d ...

  10. 使用css来开启硬件加速来提高网站性能

    一.什么是硬件加速 硬件加速就是将浏览器的渲染过程交给GPU处理,而不是使用自带的比较慢的渲染器,这样就可以使得animation与transition更加顺畅.我们可以在浏览器中用css开启硬件加速 ...