SPOJ VIDEO Video game combos 题解

SP10502 VIDEO - Video game combos

AC 自动机上的动态规划。

由题目中这句话,不难想到 AC 自动机:

sit 中出现一次指的是 sit 从某个位置起的连续子串。如果 sit 的多个位置起都是连续子串,那么算作 si 出现了多次。

考虑到每个位置有多种转移情况,且满足最优子结构性质,使用动态规划。观察后发现确定一个位置需要两个参数:目前是主串的的 i 个字符,目前在 AC 自动机中的位置 j。易得转移方程:

dp[i+1][trie[j][k]]=max(dp[i+1][trie[j][k]],dp[i][j]+h[trie[j][k]])

这里 k 是枚举 AC 自动机中的出边,h[i] 表示在 AC 自动机中的位置 i 匹配可以得到的分数。实质上,这是一个根据现在的状态推出后面状态的方程,所以 max 里会有 dp[i+1][trie[j][k]] 这一项,而后面一项则是目前枚举的状态转移之后的得分。

这里把 h[i] 预处理出来了,实际上不预处理应该也是可以的。

注意状态需要初始化为负无穷,很明显是不能从不可能的状态转移的。

#include <bits/stdc++.h>
using namespace std;
int n,k,ans,trie[310][3],ap[310],fail[310],que[310],f[1010][310],h[310],cnt=0;
char str[310];
void insert(char str[])
{
	int l=strlen(str),root=0;
	for(int i=0;i<l;i++)
	    {
	    	int id=str[i]-'A';
	    	if(!trie[root][id])trie[root][id]=++cnt;
	    	root=trie[root][id];
	    }
	ap[root]++;
}

void build_ac()
{
	int head=0,tail=0;
	fail[0]=0;
	for(int i=0;i<3;i++)
	    if(trie[0][i])que[tail++]=trie[0][i];
	while(head<tail)
	   {
	   	int now=que[head];
	   	for(int i=0;i<3;i++)
	   	    if(trie[now][i])fail[trie[now][i]]=trie[fail[now]][i],que[tail++]=trie[now][i];
	   	    else trie[now][i]=trie[fail[now]][i];
		head++;
	   }
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=0;i<n;i++)
	    {
	    	scanf("%s",str);
	    	insert(str);
		}
	build_ac();
	for(int i=0;i<=cnt;i++)
	    for(int j=i;j!=0;j=fail[j])h[i]+=ap[j];
	for(int i=0;i<=k;i++)
	    for(int j=0;j<=cnt;j++)
	        f[i][j]=-99999999;
	f[0][0]=0;
	for(int i=0;i<k;i++)
	    for(int j=0;j<=cnt;j++)
	        for(int k=0;k<3;k++)
	            f[i+1][trie[j][k]]=max(f[i+1][trie[j][k]],f[i][j]+h[trie[j][k]]);
	for(int i=0;i<=cnt;i++)
	    ans=max(ans,f[k][i]);
	printf("%d",ans);
	return 0;
}
posted @   w9095  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示