bzoj 3238

後綴數組+單調棧的應用

首先我們研究一下這個表達式,可以發現前半部分與串的情況並沒有關係,而只是跟串的長度有關,所以我們先把前半部分算出來:

\sum_{1\leq i<j \leq n}len(i)+len(j)=\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}i+j=(n-1)*n*(n+1)/2

於是我們只需計算出2\sum_{i=1}^{n-1}\sum_{j=i+1}^{n}len(lcp(i,j))即可

那麼可以發現,對於排名分別爲i,j的兩個串,他們的lcp應當是:min(height[i]-height[j])

但是這裏的時間複雜度仍然很大

我們換一個角度來思考:如果設min(height[i]-height[j])=height[k],那麼我們認爲height[k]產生了一個貢獻

所以我們可以從每一個height[k]產生了多少貢獻的角度來思考,這樣就可以把時間複雜度降到O(n)

不難發現,一個k會對一個區間產生貢獻的條件就是height[k]是所在區間的最小值

這就可以用單調棧維護了!!

但是要注意,爲了防止重複計算,我們對單調棧的兩端點的取等條件設成不一樣的(即左側算到第一個height小於等於height[k],右側算到第一個height小於height[k]的位置)

這樣找到每個點向左和向右能延伸的位置lx,rx這樣他所佔的區間個數就是(i-lx)*(rx-i)

這樣去更新就可以了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
using namespace std;
char s[500005];
int sa[500005];
int rank[500005];
int temprank[500005];
int height[500005];
int has[500005];
int v[500005];
int lx[500005],rx[500005];
int l;
bool be_same(int x,int y,int len)
{
    return x+len>l||y+len>l||rank[x]!=rank[y]||rank[x+len]!=rank[y+len];
}
void get_sa()
{
    int cnt=0;
    for(int i=1;i<=l;i++)v[i]=s[i];
    for(int i=1;i<=l;i++)has[v[i]]++;
    for(int i=0;i<128;i++)if(has[i])temprank[i]=++cnt;
    for(int i=1;i<128;i++)has[i]+=has[i-1];
    for(int i=1;i<=l;i++)
    {
        rank[i]=temprank[v[i]];
        sa[has[v[i]]--]=i;
    }
    for(int k=1;cnt!=l;k<<=1)
    {
        cnt=0;
        for(int i=1;i<=l;i++)has[i]=0;
        for(int i=1;i<=l;i++)has[rank[i]]++;
        for(int i=1;i<=l;i++)has[i]+=has[i-1];
        for(int i=l;i;i--)if(sa[i]>k)temprank[sa[i]-k]=has[rank[sa[i]-k]]--;
        for(int i=1;i<=k;i++)temprank[l-i+1]=has[rank[l-i+1]]--;
        for(int i=1;i<=l;i++)sa[temprank[i]]=i;
        for(int i=1;i<=l;i++)temprank[sa[i]]=be_same(sa[i],sa[i-1],k)?++cnt:cnt;
        for(int i=1;i<=l;i++)rank[i]=temprank[i];
    }
    for(int i=1;i<=l;i++)
    {
        if(rank[i]==1)continue;
        int j=max(1,height[rank[i-1]]-1);
        while(s[i+j-1]==s[sa[rank[i]-1]+j-1])height[rank[i]]=j++;
    }
}
void init()
{
    height[0]=height[l+1]=-0x3f3f3f3f;
    ll ret=0;
    for(int i=1;i<=l;i++)lx[i]=i-1,rx[i]=i+1;
    for(int i=2;i<=l;i++)while(height[lx[i]]>height[i])lx[i]=lx[lx[i]];
    for(int i=l;i>=2;i--)while(height[rx[i]]>=height[i])rx[i]=rx[rx[i]];
    for(int i=2;i<=l;i++)ret+=2*height[i]*(ll)((ll)(rx[i]-i)*(ll)(i-lx[i]));
    ll ans=1ll*(l-1)*l/2ll*(l+1);
    printf("%lld\n",ans-ret);
}
int main()
{
    scanf("%s",s+1);
    l=strlen(s+1);
    get_sa();
    init();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章