SP10502 VIDEO - Video game combos 题解

题目传送门

前置知识

AC 自动机

解法

多模式串匹配考虑 AC 自动机。

\(f_{i,j}\) 表示前 \(i\) 个字符,当前运行到 AC 自动机的状态 \(j\) 时的最大得分。状态转移方程为 \(f_{i,k}=\max\limits_{k \in Son(j)} \{ f_{i-1,j}+sum_{k} \}\),其中 \(sum_{k}\) 表示 fail 树上以 \(k\) 结尾的字符串个数。

最终,有 \(\max\limits_{i=0}^{tot} \{ f_{m,i} \}\) 即为所求。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
int trie[1000][5],fail[1000],sum[1000],f[2000][1000],tot=0;
char s[1000];
int val(char x)
{
	return x-'A'+1;
}
void insert(char s[],int len)
{
	int x=0,i;
	for(i=1;i<=len;i++)
	{
		if(trie[x][val(s[i])]==0)
		{
			tot++;
			trie[x][val(s[i])]=tot;
		}
		x=trie[x][val(s[i])];
	}
	sum[x]++;
}
void build()
{
	int x,i;
	queue<int>q;
	for(i=1;i<=3;i++)
	{
		if(trie[0][i]!=0)
		{
			fail[trie[0][i]]=0;
			q.push(trie[0][i]);
		}
	}
	while(q.empty()==0)
	{
		x=q.front();
		q.pop();
		for(i=1;i<=3;i++)
		{
			if(trie[x][i]==0)
			{
				trie[x][i]=trie[fail[x]][i];
			}
			else
			{
				fail[trie[x][i]]=trie[fail[x]][i];
				sum[trie[x][i]]+=sum[fail[trie[x][i]]];
				q.push(trie[x][i]);
			}
		}
	}
}
int main()
{
	int n,m,ans=0,i,j,k;
	cin>>n>>m;
	memset(f,-0x3f,sizeof(f));
	for(i=1;i<=n;i++)
	{
		cin>>(s+1);
		insert(s,strlen(s+1));
	}
	build();
	f[0][0]=0;
	for(i=1;i<=m;i++)
	{
		for(j=0;j<=tot;j++)
		{
			if(f[i-1][j]>=0)
			{
				for(k=1;k<=3;k++)
				{
					f[i][trie[j][k]]=max(f[i][trie[j][k]],f[i-1][j]+sum[trie[j][k]]);
				}
			}
		}
	}
	for(i=0;i<=tot;i++)
	{
		ans=max(ans,f[m][i]);
	}
	cout<<ans<<endl;
	return 0;
}	

后记

多倍经验:luogu P3041 [USACO12JAN] Video Game G

posted @ 2024-08-25 09:32  hzoi_Shadow  阅读(19)  评论(0)    收藏  举报
扩大
缩小