问题描述:给定m个模式串,计数包含所有模式串且长度为n的字符串的数目。

数据范围:模式串长度不超过10,m <= 10, n <= 25,此外保证答案不超过1015

分析:既然要计数给定长度且满足给定条件的字符串的数目,自然想到搜索,通过枚举每一位的字符缩减问题规模。这里有两个问题:

(1)枚举代价太高,最坏情况下需要2526次操作。

(2)先枚举出完整串再验证其是否满足条件效率太低。

通过观察,我们发现若完整字符串合法,那么在字符串构造时,每个模式串的前缀作为原串的后缀出现。为此我们考虑在搜索字符串时,

记录字符串的后缀信息,这里存储的后缀应该足够长使得若原串的某个后缀能够匹配某模式串的前缀,那么我们记录的后缀的后缀也能够

匹配。

考虑将所有模式串构造成一颗trie树,考虑trie上的状态转移:nex[i][j]表示编号为i的前缀(节点)在其后拼接上字符'a' + j后所得的串

的后缀在trie中能够匹配的最长的前缀节点编号。

并且用state[i]表示从trie根到i节点构成的串的所有后缀能够匹配模式串的集合。

我们用dp[u][S][l]标识当前枚举原字符串的第i个字符,在此之前已经匹配的模式串集合为S,当前串的后缀能够匹配trie中最深的节点编号为l,

那么可以更新dp:

for each i in 'a' to 'z':

  next_pointer = nex[l][i]

  dp[u][S][l] += dp[u + 1][S | state[next_pointer]][next_pointer]

通过使用动态规划可以解决问题(1),由于状态不超过26 * 210 * 102,且状态转移代价为26,因此总复杂度不超过2602* 210

后缀实现对整棵trie的前缀匹配,这点类似于AC自动机,实现的同样是单文本串对多模式串的匹配。

 #include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <iostream>
#include <assert.h>
#define pi acos(-1.)
using namespace std;
typedef long long ll;
const int int_inf = 0x3f3f3f3f;
const ll ll_inf = 1ll << ;
const int INT_INF = (int)((1ll << ) - );
const int mod = 1e6 + ;
const double double_inf = 1e30;
typedef unsigned long long ul;
#pragma comment(linker, "/STACK:102400000,102400000")
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define mp make_pair
#define st first
#define nd second
#define keyn (root->ch[1]->ch[0])
#define lson (u << 1)
#define rson (u << 1 | 1)
#define pii pair<int, int>
#define pll pair<ll, ll>
#define pb push_back
#define type(x) __typeof(x.begin())
#define foreach(i, j) for(type(j)i = j.begin(); i != j.end(); i++)
#define FOR(i, s, t) for(int i = (s); i <= (t); i++)
#define ROF(i, t, s) for(int i = (t); i >= (s); i--)
#define dbg(x) cout << x << endl
#define dbg2(x, y) cout << x << " " << y << endl
#define clr(x, i) memset(x, (i), sizeof(x))
#define maximize(x, y) x = max((x), (y))
#define minimize(x, y) x = min((x), (y))
#define low_bit(x) ((x) & (-x)) inline int readint(){
int x;
scanf("%d", &x);
return x;
} inline int readstr(char *s){
scanf("%s", s);
return strlen(s);
} class cmpt{
public:
bool operator () (const int &x, const int &y) const{
return x > y;
}
}; int Rand(int x, int o){
//if o set, return [1, x], else return [0, x - 1]
if(!x) return ;
int tem = (int)((double)rand() / RAND_MAX * x) % x;
return o ? tem + : tem;
} void data_gen(){
srand(time());
freopen("in.txt", "w", stdout);
int times = ;
printf("%d\n", times);
while(times--){
int n = Rand(, ), m = Rand(, );
printf("%d %d\n", n, m);
FOR(i, , n){
FOR(j, , m) printf("%c", Rand(, ) + 'a');
putchar('\n');
}
n = Rand(min(, n), ), m = Rand(min(, m), );
printf("%d %d\n", n, m);
FOR(i, , n){
FOR(j, , m) printf("%c", Rand(, ) + 'a');
putchar('\n');
}
}
} struct cmpx{
bool operator () (int x, int y) { return x > y; }
};
int debug = ;
int dx[] = {-, , , };
int dy[] = {, , -, };
//-------------------------------------------------------------------------
const int maxn = ;
const int sigma_size = ;
ll dp[][ << ][maxn * maxn];
int nex[maxn * maxn][sigma_size];
int state[maxn * maxn];
int info[maxn * maxn];
int tot;
map<string, int> mapi;
struct Trie{
int ch[maxn * maxn][sigma_size];
int idx(char c) { return c - 'a'; }
int sz;
void init() { clr(ch[], ); sz = ; clr(info, ); }
void insert(char *s){
int u = ;
while(*s){
int v = ch[u][idx(*s)];
if(!v) { ch[u][idx(*s)] = v = ++sz; clr(ch[sz], ); }
u = v;
++s;
}
if(!info[u]) info[u] = ++tot;
} char tem[];
int k; void dfs1(int u){
FOR(i, , sigma_size - ){
int v = ch[u][i];
if(!v) continue;
tem[k++] = i + 'a', tem[k] = '\0';
mapi[string(tem)] = v;
dfs1(v);
--k;
}
} void dfs2(int u){
FOR(i, , sigma_size - ){
int v = ch[u][i];
if(!v) continue;
tem[k++] = i + 'a', tem[k] = '\0';
FOR(j, , k - ){
string str = string(tem + j);
if(mapi.find(str) != mapi.end() && info[mapi[str]]) state[v] |= << (info[mapi[str]] - );
}
FOR(j, , sigma_size - ){
tem[k++] = j + 'a', tem[k] = '\0';
int ok = ;
FOR(z, , k - ){
string str = string(tem + z);
if(mapi.find(str) != mapi.end()){
nex[v][j] = mapi[str];
ok = ;
break;
}
}
if(!ok) nex[v][j] = ;
--k;
}
dfs2(v);
--k;
}
}
int S;
void getNext(){
mapi.clear();
k = , dfs1();
k = , clr(state, ), dfs2();
FOR(i, , sigma_size - ) nex[][i] = ch[][i];
}
}trie; ll power(ll a, ll p){
ll ans = ;
while(p){
if(p & ) ans *= a;
p >>= 1ll;
a = a * a;
}
return ans;
}
int n, m;
char mt[maxn][maxn]; ll dfs(int u, int S, int l){
if(dp[u][S][l] != -) return dp[u][S][l];
if(u == n) return dp[u][S][l] = S == (( << tot) - );
if(S == ( << tot) - ) return dp[u][S][l] = power(, n - u);
ll tem = ;
FOR(i, , sigma_size - ){
int next_pointer = nex[l][i];
int next_S = S | state[next_pointer];
tem += dfs(u + , next_S, next_pointer);
}
return dp[u][S][l] = tem;
} char buf[maxn << ];
int k; void printAns(int u, int S, int l){
if(!dp[u][S][l]) return;
if(u == n){
buf[k] = '\0';
puts(buf);
return;
}
FOR(i, , sigma_size - ){
int next_pointer = nex[l][i];
int next_S = S | state[next_pointer];
buf[k++] = i + 'a';
printAns(u + , next_S, next_pointer);
--k;
}
}
//------------------------------------------------------------------------- int main(){
//data_gen(); return 0;
//C(); return 0;
debug = ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(debug) freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int kase = ;
while(~scanf("%d%d", &n, &m) && n){
FOR(i, , m - ) scanf("%s", mt[i]);
trie.init(), tot = ;
FOR(i, , m - ) trie.insert(mt[i]);
trie.getNext();
clr(dp, -);
ll ans = dfs(, , );
printf("Case %d: %lld suspects\n", ++kase, ans);
if(ans <= ) k = , printAns(, , );
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
return ;
}

code:

LA 4126 Password Suspects的更多相关文章

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

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

  2. 沉迷AC自动机无法自拔之:[UVALive 4126] Password Suspects

    图片加载可能有点慢,请跳过题面先看题解,谢谢 一看到这么多模式串就非常兴奋,又是\(AC\)自动机 题目就是要求:经过 \(n\) 个节点,把所有单词都遍历一遍的方案数,和那道题差不多嘛 所以这样设: ...

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

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

  4. java实现 蓝桥杯 算法训练 Password Suspects

    问题描述 在年轻的时候,我们故事中的英雄--国王 Copa--他的私人数据并不是完全安全地隐蔽.对他来说是,这不可接受的.因此,他发明了一种密码,好记又难以破解.后来,他才知道这种密码是一个长度为奇数 ...

  5. BUAA Summer Practice 2017 #1 字符串专场

    https://vjudge.net/contest/262753#overview C - Regular Number HDU - 5972 bitset temp, temp[i]=1表示 此前 ...

  6. 获取在线人数 CNZZ 和 51.la

    string Cookies = string.Empty; /// <summary> /// 获取在线人数 (51.la统计器) /// </summary> /// &l ...

  7. 西安电子科技大学第16届程序设计竞赛 E Xieldy And His Password

    链接:https://www.nowcoder.com/acm/contest/107/E来源:牛客网 Xieldy And His Password 时间限制:C/C++ 1秒,其他语言2秒 空间限 ...

  8. 打开程序总是会提示“Enter password to unlock your login keyring” ,如何成功关掉?

    p { margin-bottom: 0.1in; line-height: 120% } 一.一开始我是按照网友所说的 : rm -f ~/.gnome2/keyrings/login.keyrin ...

  9. your password has expired.to log in you must change it

    今天应用挂了,log提示密码过期.客户端连接不上. 打开mysql,执行sql语句提示密码过期 执行set password=new password('123456'); 提示成功,但客户端仍然连接 ...

随机推荐

  1. Building a RESTful Web Service Using Spring Boot In Eclipse

    一.构建restful web service 创建Maven的java web工程,maven的pom文件加入依赖包 创建包hello Greeting.java package hello; pu ...

  2. 《30天自制操作系统》06_day_学习笔记

    harib03a: 内容没有变化 :P109 从这里开始,代码开始工程化了. 将原本300多行的bootpack.c分割成了三部分: graphic.c      : 用来处理界面图像 dsctbl. ...

  3. SQL 自动增长 identity

    create table Users( id ,),--id 从10000开始,增加长度为1 name ), ); --执行三次这个语句 insert into Users values('小昆虫') ...

  4. Good Bye 2013

    C:有点这种题的经验,先存起来相等的 D:赛后还搓了好久的代码,其实长度就100,枚举两边情况,其实A和C就涵盖了所有情况!所以到2就可以了,而且我弄出了有多少个后,和两边情况,也不知道能否或怎么凑成 ...

  5. [原创]java WEB学习笔记79:Hibernate学习之路--- 四种对象的状态,session核心方法:save()方法,persist()方法,get() 和 load() 方法,update()方法,saveOrUpdate() 方法,merge() 方法,delete() 方法,evict(),hibernate 调用存储过程,hibernate 与 触发器协同工作

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  6. SQL面向对象抽象类

    抽象类:抽象类,只为继承而出现,不定义具体的内容,只规定该有哪些东西:一般抽象类中只放置抽象方法,只规定了返回类型和参数:例: 人 - 有吃饭,睡觉方法: 男人 - 继承人抽象类,必须实现吃饭,睡觉的 ...

  7. MVC4下拉少数名族

    List<SelectListItem> nationlist = new List<SelectListItem>() { new SelectListItem(){Valu ...

  8. SLC、MLC和TLC三者的区别

    SLC=Single-LevelCell,即1bit/cell,速度快寿命长,价格超贵(约MLC3倍以上的价格),约10万次擦写寿命 MLC=Multi-LevelCell,即2bit/cell,速度 ...

  9. OpenStack 服务状态检查

    openstack服务不正常 使用命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@node-5 TimaIaas]# nova- ...

  10. UIViewController卸载过程(ios6.0之前)

    1.当应用程序收到内存不足的警告之后,程序中所有存在的UIViewController都会收到didReceiveMemoryWarning调用消息,目的是将当前不显示的View释放掉,缓解内存压力. ...