CodeForces 696D AC自动机+DP+矩阵快速幂

题意

给一组字符串,每个字符串有一个值,如果一个字符串包含这个子串,那么总值就加上这个子串的值。给一个长度,问能组成的最大值。

题解

对于要求长度1000的情况,我们可以直接DP。但是这道题不
行,这道题长度达到了10^16。我们考虑用矩阵快速幂加速。
我们很容易便能得到一个状态转移方程,dp[i][j]=max(dp[i][j],dp[i][k]+dp[k][j])。这个状态转移方程中的I,J,K都是AC自动机中的点。这个看起来像区间DP的状态转移方程其实有着很有趣的特性。我们可以发现这个状态转移4次的值,实际上就是转移两次的矩阵+转移两次的矩阵(这里的加法进行了重新定义,计算方式就是上述那个状态转移方程)。有了这个性质,我们便可以直接利用矩阵快速幂进行计算了。最后的话,取从0开始的最大值就可以了。

注意事项

需要注意进行矩阵快速幂的时候初始化矩阵不能新建一个矩阵,而是要利用计算好的A矩阵。因为如果新建一个矩阵就意味着可以从任意一个点开始,这样的话会造成结果的偏差。

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#include<stack>
#include<string>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXN 220
#define MOD 1000000007
#define EPS 1e-3
#define int LL
using namespace std;
int ch[MAXN][26];
int val[MAXN],aval[220],f[MAXN];
char s[220];
int sz;

struct Matrix{
    int mt[MAXN][MAXN];
    void init(){
        MEM(mt,0);
    }
    Matrix operator + (const Matrix b) const{
        Matrix m;
        m.init();
        UP(i,0,sz){
            UP(j,0,sz){
                m.mt[i][j]=-INF;
                UP(k,0,sz){
                    m.mt[i][j]=max(m.mt[i][j],mt[i][k]+b.mt[k][j]);
//                    if(m.mt[i][j]==8) cout<<m.mt[i][j]<<" "<<i<<" "<<j<<" "<<k<<endl;
                }
            }
        }
        return m;
    }

};

Matrix quick_pow(Matrix a,int n){
    Matrix m=a;
    n--;
    W(n){
        if(n&1) m=m+a;
        a=a+a;
        n>>=1;
    }
    return m;
}

void insert(int k){
    int u=0,n=strlen(s);
    UP(i,0,n){
        int c=s[i]-'a';
        if(!ch[u][c]){
            ch[u][c]=sz++;
        }
        u=ch[u][c];
    }
    val[u]+=aval[k];
//    cout<<u<<" "<<val[u]<<endl;
}

void getFail(){
    queue<int> q;
    MEM(f,0);
    UP(c,0,26){
        if(ch[0][c]){
            q.push(ch[0][c]);
        }
    }
    W(!q.empty()){
        int r=q.front();
        q.pop();
        UP(c,0,26){
            int u=ch[r][c];
            if(!u){ch[r][c]=ch[f[r]][c];continue;}
            q.push(u);
            int v=f[r];
            f[u]=ch[v][c];
            val[u]+=val[f[u]];
        }
    }
}

main(){
    MEM(val,0);
    MEM(ch,0);
    sz=1;
    int n,l;
    scanf("%I64d%I64d",&n,&l);
    UP(i,0,n) scanf("%I64d",&aval[i]);
    UP(i,0,n) scanf("%s",s),insert(i);
    getFail();
    Matrix mt;
    UP(i,0,sz) UP(j,0,sz) mt.mt[i][j]=-INF;
    UP(i,0,sz){
        UP(j,0,26){
            if(ch[i][j]) mt.mt[i][ch[i][j]]=val[ch[i][j]];
        }
    }
    Matrix ans=quick_pow(mt,l);
    int mx=0;
    UP(i,0,sz){
        mx=max(mx,ans.mt[0][i]);
//        cout<<ans.mt[0][i]<<" "<<i<<endl;
    }
    printf("%I64d\n",mx);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值