LA 4126 Password Suspects

问题描述:给定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:

上一篇:Birt报表分组格式调整


下一篇:Node.js怎么处理数据库中日期类型