P3041 Video Game G

P3041 Video Game G

题意:

一个字符串由,ABC三个字符构成,给出\(n\)个由A,B,C组成的组合技,求怎么安排字符串可以使得组合技最多。

思路:

因为如果当前的后缀可以的话,现在也必然可以,所以在构建AC自动机的时候,要加上cnt[ne[t]]也就是可以的后缀数量,因为是一层一层的处理,所以之前的后缀已经累加到了当前的后缀上。

实现:

#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1005;
char s[N];
int tr[N][3], idx = 1, cnt[N], nex[N], q[N], f[N][N];
void insert() //字典树中插入字符串
{
    int p = 0;
    for (int i = 1; s[i]; i++)
    {
        int t = s[i] - 'A';
        if (!tr[p][t])
            tr[p][t] = idx++;
        p = tr[p][t];
    }
    cnt[p]++;
}
//构造nex数组
void build()
{
    int hh = 1, tt = 0;
    for (int i = 0; i < 3; i++)
        if (tr[0][i])
            q[++tt] = tr[0][i];
    while (hh <= tt)
    {
        int t = q[hh++]; // i - 1
        for (int i = 0; i < 3; i++)
        {
            //注意下面要改变tr[t][i]值,所以要加引号,p相当于,当前需要匹配的位置
            int &p = tr[t][i];
            if (!p)
                // nex[t]是之前匹配到的位置
                p = tr[nex[t]][i];
            else
            {
                nex[p] = tr[nex[t]][i];
                q[++tt] = p;
                cnt[p] += cnt[nex[p]];
            }
        }
    }
}
int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++)
    {
        scanf("%s", s + 1);
        insert();
    }
    build();
    memset(f, 0xcf, sizeof(f));
    f[0][0] = 0;
    int res = 0;
    //前i个,匹配到j
    for (int i = 1; i <= k; i++)
        for (int j = 0; j < idx; j++)
            for (int h = 0; h < 3; h++)
            {
                int p = tr[j][h];
                f[i][p] = max(f[i][p], f[i - 1][j] + cnt[p]);
                if (i == k)
                    res = max(res, f[i][p]);
            }
    printf("%d", res);
    return 0;
}

posted @ 2022-08-07 17:58  zxr000  阅读(26)  评论(0编辑  收藏  举报