【题解】[USACO12JAN]视频游戏的连击Video Game Combos

好久没有写博客了,好惭愧啊……虽然这是一道弱题但还是写一下吧。

这道题目的思路应该说是很容易形成:字符串+最大值?自然联想到学过的AC自动机与DP。对于给定的字符串建立出AC自动机,dp状态dp[i][j]则表示第i位(我们求的字符串的第i位),匹配到自动机的第j位所能获得的最大值。只需要沿儿子节点与fail指针转移即可。

#include <bits/stdc++.h>
using namespace std;
#define maxn 1005
int n, m, ans, cnt;
int dp[maxn][maxn], ch[maxn][4], tag[maxn * 4], fail[maxn * 4];
string s;

void Gmax(int &x, int y)
{
    if(y > x) x = y;
}

void Trie_Ins()
{
    int len = s.length(), now = 0; 
    for(int i = 0; i < len; i ++)
    {
        if(!ch[now][s[i] - 'A']) ch[now][s[i] - 'A'] = ++ cnt;
        now = ch[now][s[i] - 'A'];
    }    
    tag[now] += 1;
}

void AC_Build()
{
    queue <int> q; 
    for(int i = 0; i < 3; i ++) 
        if(ch[0][i]) q.push(ch[0][i]);
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i = 0; i < 3; i ++)
        {
            if(ch[u][i]) 
            {
                fail[ch[u][i]] = ch[fail[u]][i];
                tag[ch[u][i]] += tag[fail[ch[u][i]]];//注意不是加1哦
                q.push(ch[u][i]);
            }
            else ch[u][i] = ch[fail[u]][i];
        }
    }
}

void DP()
{
    dp[0][0] = 0;
    for(int i = 1; i <= m; i ++)
        for(int j = 0; j <= cnt; j ++)
        {
            if(dp[i - 1][j] == -1) continue;
            for(int k = 0; k < 3; k ++)
            {
                if(ch[j][k]) Gmax(dp[i][ch[j][k]], dp[i - 1][j] + tag[ch[j][k]]);
                else Gmax(dp[i][fail[ch[j][k]]], dp[i - 1][j] + tag[fail[ch[j][k]]]);
            }
        }
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(dp, -1, sizeof(dp));
    for(int i = 1; i <= n; i ++)
    {
        cin >> s;
        Trie_Ins();
    }
    AC_Build();
    DP();
    for(int i = 0; i <= cnt; i ++) ans = max(ans, dp[m][i]);
    printf("%d
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/twilight-sx/p/8706503.html