HDU 4758 Walk Through Squares( AC自动机 + 状态压缩DP )

题意:给你两个串A,B, 问一个串长为M+N且包含A和B且恰好包含M个R的字符串有多少种组合方式,所有字符串中均只含有字符L和R。

dp[i][j][k][S]表示串长为i,有j个R,在自动机中的状态为k,包含AB的状态为S的方案个数。

PS1.之前用long long int超时了两次

PS2.把行列搞错了WA了几次

#include <cstdio>

#include <cstdlib>

#include <algorithm>

#include <cstring>



//#define LL long long int



using namespace std;



const int MAX_NODE = 1210;

const int CHILD_NUM = 2;

const int MAXN = 122;

const int MOD = 1000000007;



struct ACAutomaton

{

    int chd[MAX_NODE][CHILD_NUM]; //每个节点的儿子,即当前节点的状态转移

    int val[MAX_NODE];            //记录题目给的关键数据

    int fail[MAX_NODE];           //传说中的fail指针

    int Q[MAX_NODE<<1];           //队列,用于广度优先计算fail指针

    int ID[128];                  //字母对应的ID

    int sz;                       //已使用节点个数



    //初始化,计算字母对应的儿子ID,如:'a'->0 ... 'z'->25

    void Initialize()

    {

        fail[0] = 0;

        ID['L'] = 0;

        ID['R'] = 1;

        return;

    }

    //重新建树需先Reset

    void Reset()

    {

        memset(chd[0] , 0 , sizeof(chd[0]));

        val[0] = 0;

        sz = 1;

    }

    //将权值为key的字符串a插入到trie中

    void Insert(char *a,int key)

    {

        int p = 0;

        for ( ; *a ; a ++)

        {

            int c = ID[*a];

            if (!chd[p][c])

            {

                memset(chd[sz] , 0 , sizeof(chd[sz]));

                val[sz] = 0;

                chd[p][c] = sz ++;

            }

            p = chd[p][c];

        }

        val[p] = key;

    }

    //建立AC自动机,确定每个节点的权值以及状态转移

    void Construct()

    {

        int *s = Q , *e = Q;

        for (int i = 0 ; i < CHILD_NUM ; i ++)

        {

            if (chd[0][i])

            {

                fail[ chd[0][i] ] = 0;

                *e ++ = chd[0][i];

            }

        }

        while (s != e)

        {

            int u = *s++;

            for (int i = 0 ; i < CHILD_NUM ; i ++)

            {

                int &v = chd[u][i];

                if (v)

                {

                    *e ++ = v;

                    fail[v] = chd[ fail[u] ][i];

                    //以下一行代码要根据题目所给val的含义来写

                    val[v] |= val[ fail[v] ];

                }

                else

                {

                    v = chd[ fail[u] ][i];

                }

            }

        }

    }

} AC;



int M, N;

char str[MAXN];

int dp[2][MAXN][MAXN<<1][1<<2];



void solved()

{

    int pre = 0, cur = 1;



    memset( dp[0], 0, sizeof(dp[0]) );

    dp[0][0][0][0] = 1;

    int all = 1 << 2;

    int L = M + N;



    for ( int i = 0; i < L; ++i )

    {

        memset( dp[cur], 0, sizeof(dp[cur]) );

        for ( int j = 0; j <= M; ++j )

        for ( int k = 0; k < AC.sz; ++k )

        for ( int r = 0; r < 2; ++r )

        for ( int S = 0; S < all; ++S )

        {

            int next = AC.chd[k][r];

            dp[cur][j+r][next][ S|AC.val[next] ] += dp[pre][j][k][S];

            dp[cur][j+r][next][ S|AC.val[next] ] %= MOD;

        }



        cur ^= 1;

        pre ^= 1;

    }



    int ans = 0;

    for ( int j = 0; j < AC.sz; ++j )

    {

        ans += dp[pre][M][j][all-1];

        ans %= MOD;

    }

    printf( "%d\n", ans );



    return;

}



int main()

{

    AC.Initialize();

    int T;

    scanf( "%d", &T );

    while ( T-- )

    {

        scanf( "%d%d", &M, &N );

        AC.Reset();

        for ( int i = 0; i < 2; ++i )

        {

            scanf( "%s", str );

            AC.Insert( str, 1 << i );

        }

        AC.Construct();



        solved();

    }

    return 0;

}

 

你可能感兴趣的:(AC自动机)