正题
题目链接:https://www.luogu.com.cn/problem/P3041
题目大意
输入nnn个只有ABCABCABC的字符串,求一个长度为LLL的字符串中最多包含多少个子串是nnn个字符串中的一个
解题思路
考虑ACACAC自动机是如何进行匹配的,对于下一个字符,如果有路便往那个方向走去,然后统计该点沿条failfailfail到根节点所经过所有节点的值。然后如果没有继续往下走的路了就跳到该节点的failfailfail继续走(因为在求failfailfail的时候已经修改的TrieTrieTrie树上的数组,其实是可以直接往后走的)。
之后我们就有了dpdpdp的方法,valival_ivali表示在failfailfail树上iii节点到根节点的路径上有多少个串的结束位置。fi,jf_{i,j}fi,j表示现在填字符的iii个位置,目前在TrieTrieTrie树上节点为jjj的最大答案。之后进行dpdpdp即可
时间复杂度O(3∗∑i=1n∣si∣L)O(3*\sum_{i=1}^n |s_i|L)O(3∗∑i=1n∣si∣L)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=2100;
int n,L,cnt,ans;
int val[N],son[N][3],fail[N],f[N][N];
char s[N];
queue<int> q;
void Make(char *s){int x=0,len=strlen(s);for(int i=0;i<len;i++){int c=s[i]-'A';if(!son[x][c])son[x][c]=++cnt;x=son[x][c];}val[x]++;return;
}
void Get_Fail(){for(int i=0;i<3;i++)if(son[0][i])q.push(son[0][i]);while(!q.empty()){int x=q.front();q.pop();for(int i=0;i<3;i++)if(!son[x][i])son[x][i]=son[fail[x]][i];else{q.push(son[x][i]);int y=fail[x];fail[son[x][i]]=son[y][i];}val[x]+=val[fail[x]];}return;
}
int main()
{scanf("%d%d",&n,&L);for(int i=1;i<=n;i++){scanf("%s",s);Make(s);}Get_Fail();memset(f,0xcf,sizeof(f));f[0][0]=0;for(int i=1;i<=L;i++)for(int j=0;j<=cnt;j++)for(int k=0;k<3;k++){f[i][son[j][k]]=max(f[i][son[j][k]],f[i-1][j]+val[son[j][k]]);if(i==L)ans=max(ans,f[i][son[j][k]]);}printf("%d",ans);
}