题解 P4052 【[JSOI2007]文本生成器】

首先看到这到题,很容易想到AC自动机,但这个题它求的是方案,并且没有告诉我们母串,所以这个时候我就们可以尝试用搜索枚举每一种方案

部分代码

void dfs(int now, int j, bool flag)//now代表枚举了的文章长度,j代表AC自动机的下标,flag代表当前的文章是否合法
{
    if (now == m)
    {
        ans = (ans + flag) % MOD;
        return;
    }
    for (int i = 0; i < 26; i++)
    {
        int t = tr[j][i];
        bool is = true;
        while (t)
        {
            if (cnt[t])
            {
                dfs(now + 1, tr[j][i], true);
                is = false;
                break;
            }
            t = net[t];
        }
        if (is)
            dfs(now + 1, tr[j][i], flag);
    }
}

这个暴搜只能的20分,于是尝试优化,改成了记忆化搜索,用一个数组将之前搜过的状态存起来

int dfs(int now, int j, bool flag)
{
    if (rec[now][j][flag])
        return rec[now][j][flag];
    if (now == m)
        return flag;
    int res = 0;
    for (int i = 0; i < 26; i++)
    {
        int t = tr[j][i];
        bool is = true;
        while (t)
        {
            if (cnt[t])
            {
                res = (res + dfs(now + 1, tr[j][i], true)) % MOD;
                is = false;
                break;
            }
            t = net[t];
        }
        if (is)
            res = (res + dfs(now + 1, tr[j][i], flag)) % MOD;
    }
    rec [now][j][flag] = res;
    return res;
}

但了记忆化之后任然只能得70分,由于我tcl,已经找不出其他地方优化了,所以就只能考虑DP了,有了记忆化搜索的经验,那么DP就相对好想了,定于一个三维数组\(f_{i,j,k}\),含义同上面的搜索,i表示当前的文章长度,j表示AC自动机的位置,k表示文章是否合法,具体的转移方程放在代码里解释

完整代码

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N = 1e5 + 5, MOD = 1e4 + 7;

char str[N];
int tr[N][26], net[N], cnt[N], idx, ans, n, m;
int f[105][N][2];
queue<int> q;

void insert()
{
    int p = 0;
    for (int i = 0; str[i]; i++)
    {
        int t = str[i] - 'A';
        if (!tr[p][t])
            tr[p][t] = ++idx;
        p = tr[p][t];
    }
    cnt[p]++;
}

void build()
{
    for (int i = 0; i < 26; i++)
        if (tr[0][i])
            q.push(tr[0][i]);
    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        for (int i = 0; i < 26; i++)
        {
            int p = tr[t][i];
            if (!p)
                tr[t][i] = tr[net[t]][i];
            else 
            {
                net[p] = tr[net[t]][i];
                q.push(p);
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%s", &str);
        insert();
    }
    build();//AC自动机板子,就不解释了
    f[0][0][0] = 1;//初始状态
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j <= idx; j++)
        {
            for (int k = 0; k < 26; k++)
            {
                bool flag = false;
                int p = tr[j][k];
                while (p)
                {
                    if (cnt[p])
                    {
                        flag = true;
                        break;
                    }
                    p = net[p];
                }//判断下一个位置是否存在一个字典中的单词
                if (flag)
                    f[i + 1][tr[j][k]][1] = (f[i + 1][tr[j][k]][1] + f[i][j][0] + f[i][j][1]) % MOD;//如果存在,就把当前位置的所有方案加上
                else //不存在
                {
                    f[i + 1][tr[j][k]][0] = (f[i + 1][tr[j][k]][0] + f[i][j][0]) % MOD;//加上不合法的方案
                    f[i + 1][tr[j][k]][1] = (f[i + 1][tr[j][k]][1] + f[i][j][1]) % MOD;//加上合法的方案              
                }
            }
        }
    }
    int ans = 0;
    for (int i = 0; i <= idx; i++)
        ans = (ans + f[m][i][1]) % MOD;//将所有方案加起来        
    printf("%d", ans);
    return 0;
}
posted @ 2021-01-05 11:15  DSHUAIB  阅读(109)  评论(0)    收藏  举报