问题 K: [JSOI2009]密码

时间限制: 1 Sec  内存限制: 64 MB

题目描述

众所周知,密码在信息领域起到了不可估量的作用。对于普通的登陆口令,唯一的破解 方法就是暴力破解一逐个尝试所有可能的字母组合,但这是一项很耗时又容易被发现的工 作。所以,为了获取对方的登陆口令,在暴力破解密码之前,必须先做大量的准备工作。经 过情报的搜集,现在得到了若干有用信息,形如:

“我观察到,密码中含有字符串***。”

例如,对于一个10位的密码以及观察到的字符串hello与world,可能的密码组合为 helloworld与worldhello;而对于6位的密码以及观察到的字符串good与day,可能的 密码组合为gooday。

有了这些信息,就能够大大地减少尝试的次数了。请编一个程序,计算所有密码组合的 可能。密码中仅可能包含a - z之间的小写字母。

输入

输入数据首先输入两个整数L,N,分别表示密码的长度与观察到子串的个数。

接下来N行,每行若干个字符,描述了每个观察到的字符串。

输出

输出数据第一行为一个整数,代表了满足所有观察条件字符串的总数。

若这个数字小于等于42,则按字典顺序输出所有密码的可能情况,每行一个,

否则,只输出满足所有观察条件字符串的总数即可。

样例输入

  1. 10 2
  2. hello
  3. world

样例输出

  1. 2
  2. helloworld
  3. worldhello

提示

对于100%的数据,1<=L<=25,1<=N<=10,每个观察到的字符串长不超过10,并且保 证输出结果小于2^63。

好久没打AC自动机上的DP了……我怎么什么题都不会做啊.jpg

这道题是我今天刷到的比较好的一道题……不仅考到了dp,还考到了搜索基本功。

我们首先考虑:对于串i和j,如果j是i的子串,那么我们根本不用考虑最初单独插入进来的j串,

因为只要i串存在,j串就一定存在

那么我们可以在构建出AC自动机之后,把每个节点从fail指针能达到的节点都设为”不是单词节点“,最后再给单词节点重新编号即可。

那么接下来,我们考虑dp的过程。由于节点数,串数和串长都很小,所以我们考虑状态压缩来解决这个问题。

我们定义状态数组f[i][j][k]表示当前串长为i,位于j号节点,模式串出现情况为k的方案数。

(这种"走i步到达j节点”也是AC自动机上的常见套路之一)

那么我们事先把单词节点对应的串用二进制压好,转移到时候我们只需要这样处理即可:

f[i+1][rt->ch[u]->id][k|rt->ch[u]->val]+=f[i][j][k]

这样我们就可以搜出方案数,接下来我们考虑输出小于42的具体方案。

首先我们可以得到一个性质:若总方案树不超过42,那么最终串一定仅由给定串拼接而成。

因为如果随机字母可以存在,哪怕只有1个模式串,并且仅有1个随机字母,合法方案数在这种最小情况下也有2×26=52(>42)个

因此我们只需要用搜索进行一个dp的逆过程,看合法方案由哪个节点转移过来,并且记录一路上经过的字符,最后排序输出即可。

这真是一道很不错的题目,方式以及套路很经典,对于状压和搜索的应用都很灵活!

(Ps:我还打了一种利用状压从而不用去重的打法,放在最下面)

去重版代码见下:

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N=,L=,K=(<<)+;
  6. typedef long long LL;
  7. short l,n,len[N],bin[N],tot,cnt,num;
  8. char s[N][N];
  9. struct node
  10. {
  11. node *ch[],*f;short id,st,meaning;
  12. node(){memset(ch,,sizeof(ch)),st=,id=++cnt;}
  13. }*null,*root,*q[N*N],*data[N*N];
  14. inline node* newnode()
  15. {node *o=new node();data[cnt]=o;return o;}
  16. inline void add(int x)
  17. {
  18. node *rt=root;register int i,d;
  19. for(i=;i<len[x];++i)
  20. {
  21. d=s[x][i]-'a';
  22. if(!rt->ch[d])rt->ch[d]=newnode();
  23. rt=rt->ch[d],rt->meaning=d;
  24. }
  25. rt->st=;
  26. }
  27. inline void get_fail()
  28. {
  29. register int i,hd=,tl=;
  30. node *rt,*u;
  31. for(i=,root->f=root;i<;++i)
  32. if(root->ch[i])root->ch[i]->f=root,q[++tl]=root->ch[i];
  33. else root->ch[i]=root;
  34. while(hd<=tl)
  35. for(rt=q[hd++],u=rt->f,i=;i<;++i)
  36. if(rt->ch[i])rt->ch[i]->f=u->ch[i],q[++tl]=rt->ch[i];
  37. else rt->ch[i]=u->ch[i];
  38. for(i=;i<=cnt;++i)for(u=data[i]->f;u&&u!=null&&u!=root;u=u->f)u->st=;
  39. for(i=;i<=cnt;++i)if(data[i]->st)data[i]->st=++num;
  40. for(i=;i<=cnt;++i)if(data[i]->st)data[i]->st=bin[num-data[i]->st];
  41. }
  42. LL f[L][N*N][K];
  43. struct sol
  44. {
  45. char s[L];
  46. sol(){memset(s,,sizeof(s));}
  47. inline void print(){puts(s);}
  48. inline bool operator < (const sol &b) const
  49. {
  50. for(register int i=;i<l;++i)
  51. if(s[i]!=b.s[i])return s[i]<b.s[i];
  52. }
  53. }str[],stack;
  54. void dfs(int len,int id,int state,int now)
  55. {
  56. register int i,j,k,st;
  57. stack.s[len-]=now+'a';
  58. if(len==){str[++tot]=stack;return;}
  59. for(j=;j<=cnt;++j)
  60. if(f[len-][j][state]&&data[j]->ch[now]->id==id)
  61. dfs(len-,j,state,data[j]->meaning);
  62. if(data[id]->st)
  63. for(st=state^data[id]->st,j=;j<=cnt;++j)
  64. if(f[len-][j][st]&&data[j]->ch[now]->id==id)
  65. dfs(len-,j,st,data[j]->meaning);
  66. }
  67. inline void get_solution()
  68. {
  69. register int i,j;tot=;
  70. for(j=;j<=cnt;++j)
  71. if(f[l][j][bin[num]-])
  72. dfs(l,j,bin[num]-,data[j]->meaning);
  73. }
  74. int main()
  75. {
  76. scanf("%d%d",&l,&n);root=newnode();
  77. register int i,j,k,u,v;node *rt;LL ans=;
  78. bin[]=;for(i=;i<=n;++i)bin[i]=bin[i-]<<;
  79. for(i=;i<=n;++i)scanf("%s",s[i]),len[i]=strlen(s[i]),add(i);
  80. get_fail();
  81. for(i=,f[][][]=;i<l;++i)
  82. for(j=;j<=cnt;++j)
  83. for(rt=data[j],k=;k<bin[num];++k)
  84. if(f[i][j][k])for(u=;u<;++u)
  85. f[i+][rt->ch[u]->id][k|rt->ch[u]->st]+=f[i][j][k];
  86. for(j=;j<=cnt;++j)ans+=f[l][j][bin[num]-];
  87. printf("%lld\n",ans);
  88. if(ans<=)
  89. {
  90. get_solution();sort(str+,str+ans+);
  91. for(i=;i<=ans;++i)str[i].print();
  92. }
  93. }

不去重打法:

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4. using namespace std;
  5. const int N=,L=,K=(<<)+;
  6. typedef long long LL;
  7. short l,n,len[N],bin[N],cnt,tot,num;
  8. char s[N][N];
  9. struct node
  10. {
  11. node *ch[],*f,*fa;short id,st,meaning;
  12. node(){memset(ch,,sizeof(ch)),st=,id=++cnt;}
  13. }*root,*q[N*N],*data[N*N];
  14. inline node* newnode()
  15. {node *o=new node();data[cnt]=o;return o;}
  16. inline void add(int x)
  17. {
  18. node *rt=root;register int i,d;
  19. for(i=;i<len[x];++i)
  20. {
  21. d=s[x][i]-'a';
  22. if(!rt->ch[d])rt->ch[d]=newnode();
  23. rt->ch[d]->fa=rt,rt=rt->ch[d],rt->meaning=d;
  24. }
  25. rt->st|=bin[n-x];
  26. }
  27. inline void get_fail()
  28. {
  29. register int i,hd=,tl=;
  30. node *rt,*u;root->f=root;
  31. for(i=;i<;++i)
  32. if(root->ch[i])
  33. root->ch[i]->f=root,q[++tl]=root->ch[i];
  34. else root->ch[i]=root;
  35. while(hd<=tl)
  36. for(rt=q[hd++],u=rt->f,i=;i<;++i)
  37. if(rt->ch[i])
  38. rt->ch[i]->f=u->ch[i],q[++tl]=rt->ch[i],
  39. rt->ch[i]->st|=rt->ch[i]->f->st;
  40. else rt->ch[i]=u->ch[i];
  41. }
  42. LL f[L][N*N][K];
  43. struct sol
  44. {
  45. char s[L];
  46. sol(){memset(s,,sizeof(s));}
  47. inline void print(){puts(s);}
  48. inline bool operator < (const sol &b) const
  49. {
  50. for(register int i=;i<l;++i)
  51. if(s[i]!=b.s[i])return s[i]<b.s[i];
  52. }
  53. }str[],stack;
  54. void dfs(int len,int id,int state,int now)
  55. {
  56. register int i,j,k,st;
  57. stack.s[len-]=now+'a';
  58. if(len==){str[++tot]=stack;return;}
  59. for(j=;j<=cnt;++j)
  60. if(f[len-][j][state]&&data[j]->ch[now]->id==id)
  61. dfs(len-,j,state,data[j]->meaning);
  62. if(data[id]->st)
  63. for(st=state&(~data[id]->st),j=;j<=cnt;++j)
  64. if(f[len-][j][st]&&data[j]->ch[now]->id==id)
  65. dfs(len-,j,st,data[j]->meaning);
  66. }
  67. inline void get_solution()
  68. {
  69. register int i,j;tot=;
  70. for(j=;j<=cnt;++j)
  71. if(f[l][j][bin[n]-])
  72. dfs(l,j,bin[n]-,data[j]->meaning);
  73. }
  74. int main()
  75. {
  76. scanf("%d%d",&l,&n);root=newnode();root->fa=root;
  77. register int i,j,k,u,v;node *rt;LL ans=;
  78. bin[]=;for(i=;i<=n;++i)bin[i]=bin[i-]<<;
  79. for(i=;i<=n;++i)
  80. scanf("%s",s[i]),len[i]=strlen(s[i]),add(i);
  81. get_fail();
  82. for(i=,f[][][]=;i<=l;++i)
  83. for(j=;j<=cnt;++j)
  84. for(rt=data[j],k=;k<bin[n];++k)
  85. if(f[i-][j][k])for(u=;u<;++u)
  86. f[i][rt->ch[u]->id][k|rt->ch[u]->st]+=f[i-][j][k];
  87. for(j=;j<=cnt;++j)ans+=f[l][j][bin[n]-];
  88. printf("%lld\n",ans);
  89. if(ans<=)
  90. {
  91. get_solution();sort(str+,str+ans+);
  92. for(i=;i<=ans;++i)str[i].print();
  93. }
  94. }

[BZOJ1559]密码 AC自动机+状压的更多相关文章

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

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

  2. hdu 2825 aC自动机+状压dp

    Wireless Password Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

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

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

  4. hdu 6086 -- Rikka with String(AC自动机 + 状压DP)

    题目链接 Problem Description As we know, Rikka is poor at math. Yuta is worrying about this situation, s ...

  5. bzoj 1212: [HNOI2004]L语言 AC自动机+状压

    为什么这道题网上所有题解写的都是N*Len的trie树的暴力啊,4E的复杂度... 为什么暴力还跑这么快啊TAT.. 有一个O(Len)的做法就是先把AC自动机建出来,因为每个字典串的长度很小,所以我 ...

  6. [HNOI2006]最短母串问题——AC自动机+状压+bfs环形处理

    Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串. 32MB Input 第一行是一个正整数n(n< ...

  7. UVALive - 4126 Password Suspects (AC自动机+状压dp)

    给你m个字符串,让你构造一个字符串,包含所有的m个子串,问有多少种构造方法.如果答案不超过42,则按字典序输出所有可行解. 由于m很小,所以可以考虑状压. 首先对全部m个子串构造出AC自动机,每个节点 ...

  8. POJ1699【AC自动机+状压DP_感言】

    萌新感言: 我的天呐! 因为是AC自动机的专题所以没有管别的...硬着头皮吃那份题解(代码)..[请戳简单美丽可爱的代码(没开玩笑)] 首先讲AC自动机: tag存的是以这个节点为后缀的字符串个数(已 ...

  9. [HNOI2006]最短母串 (AC自动机+状压)

    Description 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串(S1,S2,„,Sn)都是T的子串. Input 第一行是一个正整数n(n<=12) ...

随机推荐

  1. 解决ScrollViewer嵌套的DataGrid、ListBox等控件的鼠标滚动事件无效

    C# 中,两个ScrollViewer嵌套在一起或者ScrollViewer里面嵌套一个DataGrid.ListBox.Listview(控件本身有scrollviewer)的时候,我们本想要的效果 ...

  2. Go语言2

    Go语言特点: 类型检查:编译时 运行环境:编译成机器代码直接运行 编程范式:面向接口,函数式编程,并发编程 Go并发编程 采用CSP(Communication Sequenication Proc ...

  3. 某简单易懂的人脸识别 API 的开发环境搭建和简易教程

    最近接了个人脸识别相关的项目,是基于某个非常简单易懂的人脸识别 API:face_recognition 做的.这个库接口非常傻瓜,很适合新手上手,而且可以研究其源码来学习 dlib 这个拥有更加灵活 ...

  4. Trait 是什么东西

    PHP官方手册里面写的内容是 自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait. Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制.Trait 为了减少 ...

  5. 解读Python编程中的命名空间与作用域

    变量是拥有匹配对象的名字(标识符).命名空间是一个包含了变量名称们(键)和它们各自相应的对象们(值)的字典.一个Python表达式可以访问局部命名空间和全局命名空间里的变量.如果一个局部变量和一个全局 ...

  6. [linux] LVM磁盘管理(针对xfs和ext4不同文件系统)

    简单来说就是:PV:是物理的磁盘分区VG:LVM中的物理的磁盘分区,也就是PV,必须加入VG,可以将VG理解为一个仓库或者是几个大的硬盘LV:也就是从VG中划分的逻辑分区如下图所示PV.VG.LV三者 ...

  7. NIO中的Buffer

    public abstract class Buffer { // Invariants: mark <= position <= limit <= capacity private ...

  8. sprint2 (第八天)

    今天课多,没做什么功能.这个sprint定的目标比较高,要实现的功能较多,可能完成不了目标值.因为GitHub下载和上传很慢,经常失败,所以这几天都没有更新GitHub,功能明天早点实现然后上传到Gi ...

  9. Notes of Daily Scrum Meeting(11.19)

    Notes of Daily Scrum Meeting(11.19) 现在工程项目进入尾声了,我们的项目中还有一些问题需要解决,调试修改起来进度比较慢,所以昨天就没有贴出项目 进度,今天的团队工作总 ...

  10. 实验一linux 系统简介和实验二基本概念及操作

    作业 zy e