Description
- 现给出n个字符串,总长不超过200,每个字符串有一个权值v[i].
- 构造一个长度为m(m<=1e14)的字符串,其中每出现一个给定字符串有v[i]的贡献。
- 问最大贡献是多少。
Solution
- 看到m那么大,第一眼就是矩乘。
- 然后可以发现,如果我们构造一个AC自动机,那么就变成了AC自动机上的DP问题,从一个点转移到可以到的另一个点,就相当于在答案串中加对应的字符。最后只需要转移m步即可得到答案。
- 一次x转移到y的贡献就是y的fail链上的代表的给定串的权值和。
- 用矩乘(倍增)优化这个DP就好了。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define maxn 205
using namespace std;
const ll inf=-1e15;
int n,i,j,k,len[maxn],s[maxn][maxn];
char ch;
int x,y,tot,t,w,d[maxn],to[maxn][26],fail[maxn];
ll m,ans,a[maxn],v[maxn],g[maxn][maxn],f[maxn][maxn],ff[maxn][maxn];
void build(){
tot=1;
for(i=1;i<=n;i++) {
for(x=1,j=1;j<=len[i];j++) {
y=s[i][j];
if (!to[x][y]) to[x][y]=++tot;
x=to[x][y];
}
v[x]+=a[i];
}
fail[1]=1,t=w=0;
for(i=0;i<26;i++) if (!to[1][i])
to[1][i]=1; else y=to[1][i],fail[y]=1,d[++w]=y;
while (t<w){
x=d[++t];
for(i=0;i<26;i++) if (!to[x][i]) to[x][i]=to[fail[x]][i];
else {
y=to[x][i],fail[y]=to[fail[x]][i];
v[y]+=v[fail[y]],d[++w]=y;
}
}
}
int main(){
freopen("ceshi.in","r",stdin);
scanf("%d%lld",&n,&m);
for(i=1;i<=n;i++) scanf("%lld",&a[i]);
for(i=1;i<=n;i++) {
for(ch=getchar();ch<'a'||ch>'z';ch=getchar());
for(;ch>='a'&&ch<='z';ch=getchar())
s[i][++len[i]]=ch-'a';
}
build();
for(i=1;i<=tot;i++) for(j=1;j<=tot;j++) f[i][j]=g[i][j]=inf;
for(i=1;i<=tot;i++) for(j=0;j<26;j++) if (to[i][j]&&to[i][j]!=1)
g[i][to[i][j]]=v[to[i][j]];
for(i=1;i<=tot;i++) f[i][i]=0;
for(;m;m/=2) {
if (m&1){
for(i=1;i<=tot;i++) for(j=1;j<=tot;j++) ff[i][j]=inf;
for(i=1;i<=tot;i++) for(j=1;j<=tot;j++) for(k=1;k<=tot;k++)
ff[i][k]=max(ff[i][k],f[i][j]+g[j][k]);
memcpy(f,ff,sizeof(ff));
}
for(i=1;i<=tot;i++) for(j=1;j<=tot;j++) ff[i][j]=inf;
for(i=1;i<=tot;i++) for(j=1;j<=tot;j++) for(k=1;k<=tot;k++)
ff[i][k]=max(ff[i][k],g[i][j]+g[j][k]);
memcpy(g,ff,sizeof(ff));
}
ans=0;
for(i=1;i<=tot;i++) ans=max(ans,f[1][i]);
printf("%lld",ans);
}