Luogu P3041 USACO12JAN Video Game G 题解 [ 紫 ] [ AC 自动机 ] [ 动态规划 ]
Video Games G:弱智紫题,30min 切了,dp 思路非常板。
多模式串一看肯定就是要建出 AC 自动机,然后在 fail 树里下传标记,预处理每个节点到达后的得分。
然后设计 \(dp_{i,j}\) 表示第 \(i\) 个字符,AC 自动机里匹配节点在 \(j\) 的最大答案,刷表法转移即可:
\[dp_{i+1,ch_{j,c}} \gets \max(dp_{i+1,ch_{j,c}},dp_{i,j}+con_{ch_{j,c}})
\]
时间复杂度 \(O(nk\left|S\right|)\)。
代码
#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi=pair<int,int>;
int n,m;
int ch[305][5],idx=0,ne[305],tot[305],dp[1005][305],ans=0;
char s[20];
vector<int>g[305];
void insert(char *s)
{
int p=0;
for(int i=1;s[i];i++)
{
int c=s[i]-'A';
if(ch[p][c]==0)ch[p][c]=++idx;
p=ch[p][c];
}
tot[p]++;
}
void 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++)
{
int v=ch[u][i];
if(v)ne[v]=ch[ne[u]][i],q.push(v);
else ch[u][i]=ch[ne[u]][i];
}
}
}
void dfs1(int u)
{
for(auto v:g[u])
{
tot[v]+=tot[u];
dfs1(v);
}
}
void init()
{
for(int i=1;i<=idx;i++)g[ne[i]].push_back(i);
dfs1(0);
}
int main()
{
//freopen("sample.in","r",stdin);
//freopen("sample.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>s+1;
insert(s);
}
build();
init();
memset(dp,-0x3f,sizeof(dp));
dp[0][0]=0;
for(int lv=0;lv<m;lv++)
{
for(int p=0;p<=idx;p++)
{
for(int i=0;i<3;i++)
{
int v=ch[p][i];
dp[lv+1][v]=max(dp[lv+1][v],dp[lv][p]+tot[v]);
}
}
}
for(int i=0;i<=idx;i++)ans=max(ans,dp[m][i]);
cout<<ans;
return 0;
}