http://www.lydsy.com/JudgeOnline/problem.php?id=1559

2009年的省选题虽然比起现在简单了不少,但对我来说还是很有挑战性的。

首先对于这种多串匹配问题,第一个想到的就应该是AC自动机。

还是老套路,f[i][j]表示走到字符串的第i位,现在在自动机上的第j位时的信息。增加一维n位的压位表示各个串是否都被匹配到了。

但是这里有个问题,如果S1包含了S2,那么我们只需要S1被匹配就能保证S2也被匹配,而不需要在自动机上走到S2的位置。

这样我们预处理的时候先删掉所有被包含的字符串(反正L<=25,N<=10怎样都不会T),然后直接在自动机上跑DP即可。

现在考虑ans<=42的情况,可以发现,这种情况下原串不可能有任何一个自由字母。因为只要存在一个自由字母和一个模式串,则至少存在2*26=52>42种方案。所以对于这种情况我们只需用最暴力的方法DFS穷举所有长度为L且恰好包含了每个模式串的方案并按字典序排序。

这里有一个技巧,border[i][j]表示第i个串后缀和第j个串的前缀中完全匹配的最长的串的长度(也就是KMP中的nxt[]),方便穷举。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define mem(a) memset(a,0,sizeof(a))
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; int n,m,L,ch[][],q[],num[],fail[],sz,bor[][],g1,g[],a1,len[],rk[],vis[],del[];
char str[][],a[][];
ll f[][][]; int border(int x,int y){
for (int i=min(len[x],len[y]); i>=; i--){
int flag=;
rep(j,,i-) if (str[x][len[x]-i+j]!=str[y][j]){ flag=; break; }
if (!flag) return i;
}
return ;
} void ins(int x){
int now=;
rep(i,,len[x]-){
int s=str[x][i]-'a';
if (!ch[now][s]) ch[now][s]=++sz;
now=ch[now][s];
}
num[now]=<<(x-);
} void getfail(){
int st=,ed=;
rep(i,,) if (ch[][i]) q[++ed]=ch[][i];
while (st<ed){
int x=q[++st];
rep(i,,)
if (ch[x][i]) q[++ed]=ch[x][i],fail[ch[x][i]]=ch[fail[x]][i];
else ch[x][i]=ch[fail[x]][i];
}
} void dfs(int x){
if (x>m){
a1++; int l=;
rep(i,,g1) rep(j,bor[g[i-]][g[i]],len[g[i]]-) a[a1][++l]=str[g[i]][j];
if (l!=L) a1--; return;
}
rep(i,,n) if (!del[i] && !vis[i]) vis[i]=,g[++g1]=i,dfs(x+),vis[i]=,g1--;
} bool cmp(int x,int y){
rep(i,,L) if (a[x][i]!=a[y][i]) return a[x][i]<a[y][i];
return ;
} bool chk(int x,int y){
rep(i,,len[y]-len[x]){
int j=;
for (; j<len[x]; j++) if ((str[x][j])!=str[y][i+j]) break;
if (j==len[x]) return ;
}
return ;
} void dp(){
int now=; f[][][]=;
rep(i,,L-){
now=now^; mem(f[now]);
rep(j,,sz) rep(k,,(<<n)-)
if (f[now^][j][k])
rep(l,,){
int x=ch[j][l],y=k;
if (num[x]) y|=num[x];
f[now][x][y]+=f[now^][j][k];
}
}
ll ans=; int s=;
rep(i,,n) if (!del[i]) s+=<<(i-);
rep(i,,sz) ans+=f[now][i][s];
printf("%lld\n",ans);
if (ans<=){
dfs(); rep(i,,a1) rk[i]=i;
sort(rk+,rk+a1+,cmp);
rep(i,,a1){ rep(j,,L) putchar(a[rk[i]][j]); puts(""); }
}
} int main(){
freopen("bzoj1559.in","r",stdin);
freopen("bzoj1559.out","w",stdout);
scanf("%d%d",&L,&n); m=n;
rep(i,,n) scanf("%s",str[i]),len[i]=strlen(str[i]);
rep(i,,n) rep(j,,n) bor[i][j]=border(i,j);
rep(i,,n) rep(j,,n) if (len[j]>len[i] && !del[j] && !del[i] && chk(i,j)) del[i]=,m--;
rep(i,,n) if (!del[i]) ins(i);
getfail(); dp();
return ;
}

[BZOJ1559][JSOI2009]密码(AC自动机)的更多相关文章

  1. BZOJ1559[JSOI2009]密码——AC自动机+DP+搜索

    题目描述 输入 输出 样例输入 10 2 hello world 样例输出 2 helloworld worldhello 提示 这题算是一个套路题了,多个串求都包含它们的长为L的串的方案数. 显然是 ...

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

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

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

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

  4. BZOJ1559 [JSOI2009]密码 【AC自动机 + 状压dp】

    题目链接 BZOJ1559 题解 考虑到这是一个包含子串的问题,而且子串非常少,我们考虑\(AC\)自动机上的状压\(dp\) 设\(f[i][j][s]\)表示长度为\(i\)的串,匹配到了\(AC ...

  5. [BZOJ1559]密码 AC自动机+状压

    问题 K: [JSOI2009]密码 时间限制: 1 Sec  内存限制: 64 MB 题目描述 众所周知,密码在信息领域起到了不可估量的作用.对于普通的登陆口令,唯一的破解 方法就是暴力破解一逐个尝 ...

  6. bzoj1559 [JSOI2009]密码

    题目链接:[JSOI2009]密码 我们先看第一问:输出方案数 我们把所有给出来的串丢到AC自动机里面去,然后在建出来的\(trie\)图上跑dp 由于\(n\leq 10\)我们很自然的就想到了状压 ...

  7. 【BZOJ4327】JSOI2012 玄武密码 AC自动机

    [BZOJ4327]JSOI2012 玄武密码 Description 在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河.相传一日,一缕紫气从天而至,只一瞬间便消失在了进香 ...

  8. BZOJ4327 [JSOI2012] 玄武密码 [AC自动机]

    题目传送门 玄武密码 Description 在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河.相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中.老人们说,这是玄武神 ...

  9. Vijos P1951 玄武密码 (AC自动机)

    描述 在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河.相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中.老人们说,这是玄武神灵将天书藏匿在此. 很多年后,人们终于在 ...

随机推荐

  1. 认识单点登录cas

    么是单点登录?单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分 1.登录 相比于单 ...

  2. UVA - 10494 If We Were a Child Again

    用java写的大数基本操作,java要求的格式比较严谨. import java.util.*; import java.math.*; public class Main { public stat ...

  3. lintcode 40. 用栈实现队列

    使用两个栈来回倒腾可以实现队列. AC代码: import java.util.Stack; public class Queue { private Stack<Integer> sta ...

  4. JWT 拓展

    JWT适用场景 https://www.jianshu.com/p/af8360b83a9f 适用于一次性操作的认证,颁布一个很短过期时间的JWT给浏览器. 无状态的JWT无法实现精确的在线人数统计. ...

  5. matlab求逆运算:左除( \ )和右除( / ),inv,pinv

    矩阵求逆可以使用左除(\)和右除(/),inv,pinv 首先了解需要求逆的矩阵A是否为奇异方阵 inv 若A为非奇异方阵,则存在逆矩阵,可利用inv求逆: inv(A) pinv 若需要求逆的矩阵A ...

  6. Android中注册获取验证码倒计时按钮

    public class CountDownTimerUtils extends CountDownTimer { private TextView mTextView; /** * @param t ...

  7. es6新语法Object.assign()

    1.介绍 Object.assign用于对象的合并,将源对象的所有可枚举属性复制到目标对象,只拷贝源对象自身的属性继承属性补考呗 Object.assign(target,source1,...)第一 ...

  8. 分布式系统的负载均衡以及ngnix负载均衡的五种策略

    一般而言,有以下几种常见的负载均衡策略: 一.轮询. 特点:给每个请求标记一个序号,然后将请求依次派发到服务器节点中,适用于集群中各个节点提供服务能力等同且无状态的场景. 缺点:该策略将节点视为等同, ...

  9. tyvj P1050 最长公共子序列

    题目链接:http://tyvj.cn/p/1050 题解: 裸题,只是为了测试LCS模板写对没有…… #include<cstdio> #include<cstring> # ...

  10. kvm安装准备

    到实际情况下,做虚拟化是直接做在真机上. 但实验时,可以在虚拟机上进行.(因为做实验的时候没办法连接到桥接模式的网络,所以使用了NAT方式来连接网络) 在vmware安装centos 64bit fo ...