zoukankan      html  css  js  c++  java
  • 【BZOJ3238】差异(AHOI2013)-后缀自动机+树形DP

    测试地址:差异
    做法:本题需要用到后缀自动机+树形DP。
    我们把字符串翻转,那么原串两个后缀的最长公共前缀就变成了两个前缀的最长公共后缀。注意到在后缀自动机上,两个子串的最长公共后缀就是它们在后缀链接上的LCA,那么我们先建出后缀自动机,然后在后缀链接上DP,对于每个点,求以这个点为LCA的所有前缀对对答案的贡献。状态转移方程就不写了,详见代码,算法的总时间复杂度为O(n),可以通过此题。
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,tot=1,last,pre[1000010]={0},ch[1000010][26]={0};
    int first[1000010]={0},tote=0;
    ll len[1000010],f[1000010]={0},totlen[1000010]={0},ans=0;
    char s[500010];
    struct edge
    {
        int v,next;
    }e[1000010];
    
    void init()
    {
        scanf("%s",s);
        n=strlen(s);
        for(int i=0;i<n-i-1;i++)
            swap(s[i],s[n-i-1]);
    }
    
    void extend(char c)
    {
        int p,q,np,nq;
        np=++tot;
        len[np]=len[last]+1;
        f[np]=1,totlen[np]=len[np];
        p=last;
        while(p&&!ch[p][c-'a']) ch[p][c-'a']=np,p=pre[p];
        if (!p) pre[np]=1;
        else
        {
            q=ch[p][c-'a'];
            if (len[p]+1==len[q]) pre[np]=q;
            else
            {
                nq=++tot;
                len[nq]=len[p]+1;
                pre[nq]=pre[q];
                for(int i=0;i<26;i++)
                    ch[nq][i]=ch[q][i];
                while(p&&ch[p][c-'a']==q) ch[p][c-'a']=nq,p=pre[p];
                pre[q]=pre[np]=nq;
            }
        }
        last=np;
    }
    
    void insert(int a,int b)
    {
        e[++tote].v=b;
        e[tote].next=first[a];
        first[a]=tote;
    }
    
    void build()
    {
        last=tot=1;
        for(int i=0;i<n;i++)
            extend(s[i]);
        for(int i=2;i<=tot;i++)
            insert(pre[i],i);
    }
    
    void dp(int v)
    {
        ll sum=0,sumlen=0;
        for(int i=first[v];i;i=e[i].next)
        {
            dp(e[i].v);
            ans-=(sum*f[e[i].v]*len[v])<<1;
            ans+=f[e[i].v]*sumlen+sum*totlen[e[i].v];
            sum+=f[e[i].v];
            sumlen+=totlen[e[i].v];
        }
        if (f[v])
        {
            ans-=(sum*len[v])<<1;
            ans+=sumlen+sum*totlen[v];
        }
        f[v]+=sum,totlen[v]+=sumlen;
    }
    
    int main()
    {
        init();
        build();
        dp(1);
        printf("%lld",ans);
    
        return 0;
    }
  • 相关阅读:
    统计一个字符串中字母、空格和数字的个数
    java 将一个数组中的值按逆序重新存放,例如,原来顺序为:9,5,7,4,8,要求改为:8,4,7, 5,9。
    java判断一个数是否为素数[转]
    Set集合
    List&ArrayList&LinkedList
    java_异常
    内部类&匿名内部类
    多态&抽象类&接口
    数组排序和字符串
    Java笔记_数据类型和运算符
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793490.html
Copyright © 2011-2022 走看看