图片加载可能有点慢,请跳过题面先看题解,谢谢


一看到这么多模式串就非常兴奋,又是\(AC\)自动机
题目就是要求:经过 \(n\) 个节点,把所有单词都遍历一遍的方案数,和那道题差不多嘛
所以这样设:\(f[i][j][k]\) 为,走了 \(i\) 个节点,当前点在 \(j\),单词的经过情况为 \(k\)(一个二进制数)时的方案数
答案很显然是:\(\sum_{i=1}^{size}f[n][i][(1<<m)-1]\)
转移也很显然:\(f[i+1][Son][s|val[Son]]+=f[i][j][s]\)
$
$
但是这道题有点麻烦的就是,它需要输出方案,上面那样子做不太好记录路径
这里考虑反过来搞,这样转移:\(f[i][j][s]+=f[i+1][Son][s|val[Son]]\),答案就变成了\(f[0][0][0]\)
这样做的话,就可以保证只有在有效路径上,\(f\) 值才不为\(0\),那么方案就很好办了
$
$

//made by Hero_of_Someone
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<set>
#define ll long long
using namespace std;

int t,n,m;

struct Trie{
   char o[26];
   int root,size;
   ll f[26][110][1<<10];
   int son[110][30],fail[110],val[110];

   void init(){
      size=1; root=0;
      memset(son,0,sizeof(son));
      memset(val,0,sizeof(val));
      memset(fail,0,sizeof(fail));
      memset(f[n],0,sizeof(f[n]));
   }

   int idx(char c){ return c-'a'; }

   void insert(char* s,int v){
      int cur=root;
      for(int i=0;s[i];i++){
         int id=idx(s[i]);
         if(!son[cur][id]) son[cur][id]=size++;
         cur=son[cur][id];
      }
      val[cur]|=1<<v; return ;
   }

   void build(){
      int que[110];
      int hd=0,tl=0;
      for(int i=0;i<26;i++)
         if(son[root][i]){
            fail[son[root][i]]=root;
            que[tl++]=son[root][i];
         }
         else son[root][i]=root;

      while(hd<tl){
         int cur=que[hd++];
         for(int i=0;i<26;i++){
            int Son=son[cur][i];
            if(Son){
               int f=fail[cur];
               while(f && !son[f][i]) f=fail[f];
               fail[Son]=son[f][i];
               val[Son]|=val[fail[Son]];
               que[tl++]=Son;
            }
            else son[cur][i]=son[fail[cur]][i];
         }
      }
   }

   ll dp(){
      for(int i=0;i<size;i++) f[n][i][(1<<m)-1]=1;
      for(int i=n-1;i>=0;i--)
         for(int j=0;j<size;j++)
            for(int s=0;s<(1<<m);s++){
               f[i][j][s]=0;
               for(int k=0;k<26;k++){
                  int Son=son[j][k];
                  int ss=s|val[Son];
                  f[i][j][s]+=f[i+1][Son][ss];
               }
            }
      return f[0][0][0];
   }

   void print(int i,int j,int s){
      if(i==n){
         for(int k=0;k<n;k++) printf("%c",o[k]);
         puts("");
         return ;
      }
      for(int k=0;k<26;k++){
         int Son=son[j][k];
         int ss=s|val[Son];
         if(f[i+1][Son][ss]){
            o[i]=k+'a';
            print(i+1,Son,ss);
         }
      }
   }

}AC;

set<string>map;

void init(){
   AC.init(),map.clear();
   int cnt=0;
   for(int i=0;i<m;i++){
      char s[15];
      scanf("%s",s);
      string S=s;
      if(!map.count(S)){
         AC.insert(s,cnt++);
         map.insert(S);
      }
   }
   m=cnt;
}

void work(){
   AC.build();
   ll ans=AC.dp();
   printf("Case %d: %lld suspects\n",++t,ans);
   if(ans<=42) AC.print(0,0,0);
}

int main(){ while(scanf("%d%d",&n,&m)&&n){ init(); work(); } return 0; }

沉迷AC自动机无法自拔之:[UVALive 4126] Password Suspects的更多相关文章

  1. 沉迷AC自动机无法自拔之:[BZOJ2434] [Noi2011] 阿狸的打字机

    如标题所言,我已经沉迷于AC自动机无法自拔了... 这又是一道AC自动的题,红红火火恍恍惚惚 这题目做起来真舒服 简单概括一下:\(AC\)自动机\(fail\)树上树链剖分\(+\)树状数组 这种类 ...

  2. 沉迷AC自动机无法自拔之:穿越广场 square

    如标题所言,我已经沉迷于AC自动机无法自拔了... 这又是一道AC自动的题,红红火火恍恍惚惚 穿越广场 [问题描述] L 国的仪仗队要穿越首都广场了.首都广场可以看做是一块 N*M 的矩形网格,仪仗队 ...

  3. 沉迷AC自动机无法自拔之:[UVA 11468] Substring

    图片加载可能有点慢,请跳过题面先看题解,谢谢 这个鬼题目,上一波套路好了 先用题目给的模板串建\(AC\)自动机,把单词结尾标记为 \(val=1\),然后在建好的\(AC\)自动机上跑 \(dp\) ...

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

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

  5. LA 4126 Password Suspects

    问题描述:给定m个模式串,计数包含所有模式串且长度为n的字符串的数目. 数据范围:模式串长度不超过10,m <= 10, n <= 25,此外保证答案不超过1015. 分析:既然要计数给定 ...

  6. DP(记忆化搜索) + AC自动机 LA 4126 Password Suspects

    题目传送门 题意:训练指南P250 分析:DFS记忆化搜索,范围或者说是图是已知的字串构成的自动机图,那么用 | (1 << i)表示包含第i个字串,如果长度为len,且st == (1 ...

  7. UVALive 4670 Dominating Patterns --AC自动机第一题

    题意:多个模板串,一个文本串,求出那些模板串在文本串中出现次数最多. 解法:AC自动机入门模板题. 代码: #include <iostream> #include <cstdio& ...

  8. UVALive - 3490 Generator (AC自动机+高斯消元dp)

    初始有一个空串s,从前n个大写字母中不断随机取出一个字母添加到s的结尾,出现模式串t时停止,求停止时s的长度期望. 这道题解法不唯一,比较无脑的方法是对模式串t建一个单串AC自动机,设u为自动机上的一 ...

  9. UVALive 4670 Dominating Patterns (AC自动机)

    AC自动机的裸题.学了kmp和Trie以后不难看懂. 有一些变化,比如0的定义和f的指向,和建立失配边,以及多了后缀连接数组last.没有试过把失配边直接当成普通边(一开始还是先这样写吧). #inc ...

随机推荐

  1. 20155304《网络对抗》Exp4 恶意代码分析

    20155304<网络对抗>Exp4 恶意代码分析 实践内容 1.系统运行监控 1.1使用schtasks指令监控系统运行 我们在C盘根目录下建立一个netstatlog.bat的文本文件 ...

  2. Windows下TeX Live + Sublime Text 3 + Sumatra PDF配置

    本文写给我的师弟们,如何自己动手配置LaTeX环境(通过LeX Live + Sublime Text 3 + Sumatra PDF). 1.TeX Live 配置 首先从TeX Live 下载IS ...

  3. POJ 2299

    上课讲了下数据结构,因为暂时没找到分块的板子题,所以做一下这道题加深一下对树状数组的理解. 题意就是求逆序对,从逆序对的定义就可以看出,方法有两种:归并 or 树状数组. 感觉树状数组更高级一点,写起 ...

  4. [CTSC2006]歌唱王国

    [CTSC2006]歌唱王国 Tags:题解 题意 链接:在空串后不断随机添加字符,直到出现串\(S_i\)为止.求最终串的期望长度.\(\sum |S_i|\le 5*10^6\) 题解 以下内容来 ...

  5. mfc CListCtrl 报表格式

    知识点: CListCtrl报表格式 CListCtrl报表格式添加列 CListCtrl报表格式添加行 CListCtrl报表格式设置单元格 一.CListCtrl报表格式 类名:SysListVi ...

  6. mfc 友元类

    知识点 继承类成员的访问级别 友元类 继承访问控制: 基类 派生类(能否访问) public private protected 派生类类 派生类对象 派生类 派生类对象 派生类类 派生类对象 pri ...

  7. RHEL6 最小化系统 编译安装部署zabbix (mysql)

    RHEL6 最小化系统 编译安装部署zabbix (mysql)官方说明详细见:https://www.zabbix.com/documentation/4.0/manual/installation ...

  8. C语言学习之结构体

    前言 一直以来,C语言的学习都在入门阶段,只用到数组.函数.循环.选择.位运算这些基本的知识,较少用到指针.预处理.结构体.枚举类型.文件操作等这些C语言的精髓内容,现在想想真不敢说自己熟练掌握C语言 ...

  9. libgdx判断actor与circle是否重叠

    实质是检测矩形与circle是否重叠 基本函数,判断点是否在circle中 public static boolean IsInside( float x, float y, Circle circl ...

  10. Java虚拟机笔记(五):JVM中对象的分代

    为什么要分代 为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能.你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我们要找到哪些对象没用, ...