问题描述:给定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. linux:磁盘的分割、检验、格式化与挂载

    新增一颗磁碟: 1.对磁碟进行分割,以建立可用的partition 2.对该分割槽partition进行格式化(format),以建立系统可用的filesystem 3.若要仔细点,可对刚刚建立的fi ...

  2. CSS文档流与块级元素和内联元素

    CSS文档流与块级元素(block).内联元素(inline),之前翻阅不少书籍,看过不少文章, 看到所多的是零碎的CSS布局基本知识,比较表面.看过O'Reilly的<CSS权威指南>, ...

  3. 在xocde运行profile 遇到"Existing default base temp directory '/Library/Caches/com.apple.dt.instruments' has insufficient privileges for user id 505. Please have the owner delete this directory"

    找到这篇文章 http://stackoverflow.com/questions/34153795/xcode-7-unable-to-open-instruments-from-developer ...

  4. PostgreSQL应用相关问题解决

    PostgreSQL中是否区分聚簇索引与非聚簇索引的问题? 答:PostgreSQL中区分聚簇索引与非聚簇索引. 示例如下: 创建聚族索引: CREATE INDEX test_ind ON yy ( ...

  5. 转:Python requests 快速入门

    迫不及待了吗?本页内容为如何入门Requests提供了很好的指引.其假设你已经安装了Requests.如果还没有, 去 安装 一节看看吧. 首先,确认一下: ·Requests 已安装 ·Reques ...

  6. 2-sat按照最小字典序输出可行解(hdu1814)

    Peaceful Commission Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  7. Python学习总结14:时间模块datetime & time & calendar (一)

    Python中的常用于处理时间主要有3个模块datetime模块.time模块和calendar模块. 一.time模块 1. 在Python中表示时间的方式 1)时间戳(timestamp):通常来 ...

  8. .net 反编译工具

    遇到一个需求,做一个专门访问自己网站的浏览器给用户使用,这个浏览器提供登录功能.此时是一个安装在客户端的exe程序,做登录验证要用到webservice,不能将验证逻辑写入exe中,否则客户端可以利用 ...

  9. UVALive 7148 LRIP【树分治+线段树】

    题意就是要求一棵树上的最长不下降序列,同时不下降序列的最小值与最大值不超过D. 做法是树分治+线段树,假设树根是x,y是其当前需要处理的子树,对于子树y,需要处理出两个数组MN,MX,MN[i]表示以 ...

  10. JSon_零基础_007_将JSon格式的"数组"字符串转换为Java对象"数组"

    将JSon格式的"数组"字符串转换为Java对象"数组". 应用此技术从一个json对象字符串格式中得到一个java对应的对象. JSONObject是一个“n ...