给定 \(N\) 个模式串。一个母串的分数定义为能与模式串匹配的次数,可以与同一个模式串多次匹配。问一个长度为 \(K\) 的母串最多能获得多少分。\(N\leq 20,K \leq 1000\)
Solution
考虑在 AC 自动机上 DP,令 \(f[i][j]\) 表示走了 \(i\) 个字符,到达结点 \(j\),则
\[
f[i][j]+val[ch[j][k]] \to f[i+1][ch[j][k]]
\]
其中 \(val[p]\) 表示结点 \(p\) 到 fail 树根的链上有多少个末端
#include <bits/stdc++.h>
using namespace std;
int val[1005],ch[1005][3],fi[1005],dp[1005][1005],ind,n,m;
char str[1005];
void insert(char *s){
int len=strlen(s),p=0;
for(int i=0;i<len;i++){
int v=s[i]-'A';
if(!ch[p][v]) ch[p][v]=++ind;
p=ch[p][v];
}
val[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 p=q.front(); q.pop();
for(int i=0;i<3;i++)
if(ch[p][i]) fi[ch[p][i]]=ch[fi[p]][i], q.push(ch[p][i]);
else ch[p][i]=ch[fi[p]][i];
val[p]+=val[fi[p]];
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",&str), insert(str);
build();
memset(dp,0x80,sizeof dp);
for(int i=0;i<=m;i++) dp[i][0]=0;
for(int i=1;i<=m;i++){
for(int j=0;j<=ind;j++){
for(int k=0;k<3;k++){
dp[i][ch[j][k]]=max(dp[i][ch[j][k]],dp[i-1][j]+val[ch[j][k]]);
}
}
}
int ans=0;
for(int i=0;i<=ind;i++)
ans=max(ans,dp[m][i]);
printf("%d\n",ans);
}