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

题目链接:BZOJ - 1559

 

题目分析

将给定的串建成AC自动机,然后在AC自动机上状压DP。

转移边就是Father -> Son 或 Now -> Fail。

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

状态 k 是一个二进制压缩的,表示已经包含了哪些给定串的整数。

然后...输出方案....这个太麻烦了...我是从最后状态DFS向前反推。

另外的问题是我写的AC自动机DP无法正确处理给定串存在串 A 包含串 B 的情况,所以我必须将被其他给定串包含的给定串忽略。

要去掉所有被其他某个给定串包含的给定串。需要在建完Fail之后从每个节点向Fail一直走到Root,将一路上的所有节点都设定为不是给定串。

当然要记录一下某个节点沿Fail到Root的路径已经被处理的话就 break。

我刚开始写的代码只能处理一个串是另一个串前缀或后缀的情况,被包含在中间的情况不能处理,但是仍然在BZOJ AC了。

 

代码

#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编辑  收藏  举报