洛谷P4248 [AHOI2013]差別(後綴自動機求lcp之和)

題目見此c++

 

題解:首先全部後綴都在最後一個np節點,而後他們都是從1號點出發沿一些字符邊到達這個點的,因此下文稱1號點爲根節點,咱們思考一下何時會產生lcp,顯然是當他們從根節點開始一直跳相同節點的時候,因此思路就是先找出每一個節點被幾個後綴通過,這顯然把邊反轉倒着找就能夠了,而後他會被出現次數sz個串通過。spa

出現次數等於parent樹子樹中np類節點的個數,這跑個dfs就行了,一個相同前綴產生的貢獻是sz*(sz-1)/2code

而後思考一個點可能表明多個子串,可是他們的出現次數都是相同的,因此單個點的貢獻爲上面的單個貢獻再乘上一個有幾個子串blog

子串的個數爲parent樹父親節點的最大長度減去該節點的最大長度get

這樣子在從根開始dfs,若是通過某個點只有一個後綴通過,就說明lcp結束了,就不用再搜該點了。it

上面就求出了lcp的和ast

至於前面那個式子,只須要打個表找個規律發現是(n-1)*n*(n+1)/2就能夠了class

雖然常數大點可是仍是後綴自動機複雜度的di

 

但其實不用這麼複雜,只要翻過來就能夠建出原串後綴樹,lcp就是後綴樹的兩個節點的lca,跑個樹形dp就能夠了。思考

 

代碼由於沒用鏈式前向星存邊因此不開o2會t,但仍是貼一下吧

#include<bits/stdc++.h>
#define N 1000010
using namespace std;

int n;
int gg=0;

struct SAM
{
    struct point
    {
        int son[26],fa,len,mx;
    }t[N];
    
    int cnt=1,last=1;
    int f[N],sz[N];
    bool vis[N];
    vector<int> g[N],e[N];
    long long lcp=0ll; 
    
    void add(int c)
    {
        int p=last;
        int np=++cnt;
        t[np].len=t[p].len+1;
        sz[np]=1;
        while(p&&(!t[p].son[c]))
        {
            t[p].son[c]=np;
            p=t[p].fa;
        }
        if(!p) t[np].fa=1;
        else
        {
            int q=t[p].son[c],nq;
            if(t[p].len+1==t[q].len)
            {
                t[np].fa=q;
            }
            else
            {
                nq=++cnt;
                t[nq]=t[q];
                t[nq].len=t[p].len+1;
                t[q].fa=t[np].fa=nq;
                while(p&&(t[p].son[c]==q))
                {
                    t[p].son[c]=nq;
                    p=t[p].fa; 
                }
            }
        }
        last=np;
    }
    
    void dfs(int now)
    {
        t[now].mx=t[now].len-t[t[now].fa].len;
        for(int i=0;i<26;i++)
        {
            if(t[now].son[i]) e[t[now].son[i]].push_back(now);
        }
        for(int i=0;i<g[now].size();i++)
        {
            dfs(g[now][i]);
            sz[now]+=sz[g[now][i]];
        }
    }
    
    void dfs1(int now)
    {
        vis[now]=1;
        for(int i=0;i<e[now].size();i++)
        {
            f[e[now][i]]++;
            if(!vis[e[now][i]])
            {
                dfs1(e[now][i]);
            }
        }
    }
    
    void dfs3(int now)
    {
        vis[now]=1;
        if(f[now]) lcp+=t[now].mx*(1ll*sz[now]*(sz[now]-1)/2);
        for(int i=0;i<26;i++)
        {
            if(f[t[now].son[i]]&&sz[t[now].son[i]]>1&&(!vis[t[now].son[i]]))
            {
                dfs3(t[now].son[i]);
            }
        }
    }
    
    void solve()
    {
        for(int i=1;i<=cnt;i++) g[t[i].fa].push_back(i);
        dfs(1);
        sz[1]=0;
        memset(vis,0,sizeof(vis));
        dfs1(last);
        memset(vis,0,sizeof(vis)); 
        dfs3(1);
        long long len=1ll*n*(n-1)*(n+1)/2;
        printf("%lld\n",len-2*lcp);
    }
    
}sam;

char s[500050];

int main()
{
    scanf("%s",s);
    n=strlen(s);
    for(int i=0;i<n;i++)
    {
        sam.add(s[i]-'a');
    }
    sam.solve(); 
} 
相關文章
相關標籤/搜索