【CF696D】 Legen...
Legen...
题意
翻译版本:
Solution
首先肯定是AC机上面的DP了
状转的柿子是显然的:\(dp[k][i]=max(dp[k][i],dp[k-1][j]+val[i][j])\),其中i和j都是AC自动机里面的节点,\(val[i][j]\)表示从i节点到j节点可以增加的贡献
然后可以考虑一下用类似于矩阵乘法的方式转移:
一个矩阵\(A\),其中i行j列表示一开始从i节点转移来,然后转移到j节点的最大答案
然后每次转移就是\(now_A[i][j]=max(now_A[i][j],last_A[i][k]+val[k][j])\)
然后就可以用类似于矩阵快速幂的方法转移了
总共的时间复杂度是\(O(log_n(m \times l) ^ 3)\)
l是平均长度,m字符串是个数
因为\(m \times l\)最大只有200,所以可以通过这个题
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int tot;
struct node{
int a[210][210];
node(){memset(a,-0x3f,sizeof(a));}
node operator * (node b){
node ret;
for(int i=0;i<=tot;++i){
for(int j=0;j<=tot;++j){
for(int k=0;k<=tot;++k){
ret.a[i][j]=max(ret.a[i][j],a[i][k]+b.a[k][j]);
}
}
}
return ret;
}
};
node fastpow(node a,int k){
node ret;
for(int i=0;i<=tot;++i){
ret.a[i][i]=0;
}
while(k){
if(k&1)ret=ret*a;
a=a*a;
k>>=1;
}
return ret;
}
int ch[210][26];
int val[6010];
char s[210];
int rt;
void insert(int w){
int len=strlen(s+1);
int now=rt;
for(int i=1;i<=len;++i){
int son=s[i]-'a';
if(!ch[now][son]){
ch[now][son]=++tot;
}
now=ch[now][son];
}
val[now]+=w;
}
int fail[6010];
void getfail(){
queue<int> q;
for(int i=0;i<26;++i){
if(ch[rt][i])q.push(ch[rt][i]);
}
while(!q.empty()){
int u=q.front();
q.pop();
val[u]+=val[fail[u]];
for(int i=0;i<26;++i){
if(ch[u][i]){
fail[ch[u][i]]=ch[fail[u]][i];
q.push(ch[u][i]);
}
else ch[u][i]=ch[fail[u]][i];
}
}
}
node start;
void init(){
for(int i=0;i<=tot;++i){
for(int j=0;j<26;++j){
int x=ch[i][j];
start.a[i][x]=val[x];
}
}
}
int aaaa[210];
signed main(){
int n,m;
scanf("%I64d%I64d",&m,&n);
for(int i=1;i<=m;++i){
scanf("%I64d",&aaaa[i]);
}
for(int i=1;i<=m;++i){
memset(s,0,sizeof(s));
scanf("%s",s+1);
insert(aaaa[i]);
}
getfail();
init();
start=fastpow(start,n);
int final=0;
for(int i=1;i<=tot;++i){
final=max(final,start.a[0][i]);
}
printf("%I64d\n",final);
}
(是CF题只好I64d了)