• [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
    ", 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
    ", 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
    ", 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
    ", Sol[i].str);
            }
        }
        return 0;   
    }
    

      

  • 相关阅读:
    poj3613 求经过n条边的最短路 ----矩阵玩出新高度 。
    牛客练习赛43 Tachibana Kanade Loves Game (简单容斥)
    牛客练习赛43 Tachibana Kanade Loves Review C(最小生成树Kruskal)
    牛客练习赛43 Tachibana Kanade Loves Probability(快速幂)
    哈尔滨工程大学ACM预热赛 G题 A hard problem(数位dp)
    poj 3252 Round Numbers(数位dp 处理前导零)
    hdu 3652 B-number(数位dp)
    poj 3666 Making the Grade(离散化+dp)
    poj 3186 Treats for the Cows(dp)
    poj 1661 Help Jimmy (dp)
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4332383.html
Copyright © 2020-2023  润新知