# [BZOJ 1559] [JSOI2009] 密码 【AC自动机DP】

### 题目分析

f[i][j][k] ，表示到了字符串第 i 位，在AC自动机的第 j 个节点上，状态为 k 的方案数。

### 代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>

using namespace std;

const int MaxL = 15, MaxM = 15, MaxC = 27, MaxN = 25 + 5, MaxNode = 255 + 5;

typedef long long LL;

int n, m, l, Index, Tot, MT, Top;

LL Ans;
LL f[MaxN][MaxNode][1024 + 15];

char S[MaxL];

struct Str
{
char str[MaxN];
} Sol[50], DS;

bool Cmp(Str s1, Str s2)
{
for (int i = 0; i < n; ++i)
{
if (s1.str[i] == s2.str[i]) continue;
return s1.str[i] < s2.str[i];
}
return false;
}

struct Trie
{
int Num, ID, c, isStr;
bool Ed;
Trie *Child[MaxC], *Fail;
} TA[MaxNode], *P = TA, *Root, *Zero;

void Insert(char *S, int l)
{
Trie *Now = Root;
int t;
for (int i = 1; i <= l; ++i)
{
t = S[i] - 'a';
if (Now -> Child[t] == NULL)
{
++P; P -> Num = 0; P -> ID = ++Tot; P -> c = t;
Now -> Child[t] = P;
}
Now = Now -> Child[t];
}
Now -> isStr = 1;
}

void Init_AC()
{
Index = 0; Tot = 0;
Zero = P; //ID : 0
Root = ++P; Root -> ID = ++Tot; //ID : 1
for (int i = 0; i < 26; ++i) Zero -> Child[i] = Root;
Zero -> Fail = NULL;
for (int i = 0; i < 26; ++i) Root -> Child[i] = NULL;
Root -> Fail = Zero;
}

queue<Trie *> Q;

void Build_Fail()
{
while (!Q.empty()) Q.pop();
Q.push(Root);
Trie *Now, *Temp;
while (!Q.empty())
{
Now = Q.front(); Q.pop();
for (int i = 0; i < 26; ++i)
{
if (Now -> Child[i] == NULL) Now -> Child[i] = Now -> Fail -> Child[i];
else
{
Now -> Child[i] -> Fail = Now -> Fail -> Child[i];
Q.push(Now -> Child[i]);
}
}
}
for (Trie *j = TA; ; ++j)
{
Temp = j -> Fail;
while (Temp != NULL && Temp != Root && Temp != Zero)
{
if (Temp -> Ed) break;
Temp -> Ed = true;
Temp -> isStr = -1;
Temp = Temp -> Fail;
}
if (j == P) break;
}
for (Trie *j = TA; ; ++j)
{
if (j -> isStr == 1) j -> Num = ++Index;
else j -> Num = 0;
if (j == P) break;
}
}

void DP()
{
f[0][1][0] = 1;
MT = (1 << Index) - 1;
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= Tot; ++j)
for (int k = 0; k <= MT; ++k)
if (f[i][j][k])
{
for (int t = 0; t < 26; ++t)
{
if (TA[j].Child[t] -> Num == 0) f[i + 1][TA[j].Child[t] -> ID][k] += f[i][j][k];
else f[i + 1][TA[j].Child[t] -> ID][k | (1 << (TA[j].Child[t] -> Num - 1))] += f[i][j][k];
}
}
Ans = 0;
for (int i = 1; i <= Tot; ++i) Ans += f[n][i][MT];
}

void DFS(int l, int x, int y, int t)
{
//printf("Begin %d %d %d %c\n", l, x, y, t + 'a');
DS.str[l - 1] = 'a' + t;
if (l == 1)
{
Sol[++Top] = DS;
return;
}
for (int i = 1; i <= Tot; ++i)
if (f[l - 1][i][y] && TA[i].Child[t] -> ID == x)
{
if (i == 1) for (int j = 0; j < 26; ++j) DFS(l - 1, i, y, j);
else DFS(l - 1, i, y, TA[i].c);
}
int yy;
if (TA[x].Num != 0)
{
yy = y - (1 << (TA[x].Num - 1));
for (int i = 1; i <= Tot; ++i)
if (f[l - 1][i][yy] && TA[i].Child[t] -> ID == x)
{
if (i == 1) for (int j = 0; j < 26; ++j) DFS(l - 1, i, yy, j);
else DFS(l - 1, i, yy, TA[i].c);
}
}
//printf("End %d %d %d %c\n", l, x, y, t + 'a');
}

void Get_Sol()
{
Top = 0;
for (int i = 1; i <= Tot; ++i)
if (f[n][i][MT])
{
if (i == 1)
{
for (int j = 0; j < 26; ++j)
DFS(n, i, MT, j);
}
else DFS(n, i, MT, TA[i].c);
}
}

int main()
{
scanf("%d%d", &n, &m);
Init_AC();
Index = 0;
for (int i = 1; i <= m; ++i)
{
scanf("%s", S + 1);
l = strlen(S + 1);
Insert(S, l);
}
Build_Fail();
DP();
printf("%lld\n", Ans);
if (Ans <= 42)
{
Get_Sol();
sort(Sol + 1, Sol + Ans + 1, Cmp);
for (int i = 1; i <= Ans; ++i)
{
Sol[i].str[n] = 0;
printf("%s\n", Sol[i].str);
}
}
return 0;
}


posted @ 2015-03-12 14:42  JoeFan  阅读(485)  评论(0编辑  收藏  举报