BZOJ3238: [Ahoi2013]差异

【传送门:BZOJ3238


简要题意:

  给出一个长度为n的字符串,设Ti为以第i个字符为开头的后缀,lcp(x,y)为x字符串和y字符串的最长公共前缀

  求$$\sum_{1<=i<j<=n}len(Ti)+len(Tj)-2*lcp(i,j)$$


题解:

  后缀数组吧?

  先打个板子

  显然可以先把Σlen(Ti)+len(Tj)直接求出来

  ans=(len-1)*(len+1)*len/2

  接下来处理最长公共前缀和的问题

  先求出height数组,表示字典序排第i-1个的后缀与排第i个的后缀的最长公共前缀

  如果我们要求第i个到第j个的最长公共前缀,就等于min(height[i+1],height[i+2]....height[j])

  我们就用两个单调栈来处理每个height值最多往左右延伸的长度,分别用l[i],r[i]记录

  然后ans-=2*Σ(i-l[i]+1)*(r[i]-i+1)*height[i]

  因为以i往左最多延伸l[i],往右最多延伸r[i]的情况下,包含i的区间有(i-l[i]+1)*(r[i]-i+1)个

  还要注意一个地方,在求的时候,假如相邻的值相等,那么答案就会多算,所以有求r的时候,单调栈的条件要变成<=


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
char a[510000];
int s[510000];
int sa1[510000],sa2[510000];
int Rank[510000];
int Rsort[510000];
int tt[510000];
void getsa(int n,int m)
{
    memcpy(Rank,s,sizeof(Rank));
    memset(Rsort,0,sizeof(Rsort));
    for(int i=1;i<=n;i++) Rsort[Rank[i]]++;
    for(int i=1;i<=m;i++) Rsort[i]+=Rsort[i-1];
    for(int i=n;i>=1;i--) sa1[Rsort[Rank[i]]--]=i;
    int ln=1,p=0;
    while(p<n)
    {
        int k=0;
        for(int i=n-ln+1;i<=n;i++) sa2[++k]=i;
        for(int i=1;i<=n;i++) if(sa1[i]-ln>0) sa2[++k]=sa1[i]-ln;
        memset(Rsort,0,sizeof(Rsort));
        for(int i=1;i<=n;i++) Rsort[Rank[i]]++;
        for(int i=1;i<=m;i++) Rsort[i]+=Rsort[i-1];
        for(int i=n;i>=1;i--) sa1[Rsort[Rank[sa2[i]]]--]=sa2[i];
        for(int i=1;i<=n;i++) tt[i]=Rank[i];
        Rank[sa1[1]]=1;p=1;
        for(int i=2;i<=n;i++)
        {
            if(tt[sa1[i]]!=tt[sa1[i-1]]||tt[sa1[i]+ln]!=tt[sa1[i-1]+ln]) p++;
            Rank[sa1[i]]=p;
        }
        ln*=2;m=p;
    }
}
LL height[510000];
int list[510000];
int l[510000],r[510000];
int main()
{
    scanf("%s",a+1);
    int len=strlen(a+1);
    for(int i=1;i<=len;i++) s[i]=a[i]-'a'+1;
    getsa(len,200);
    LL ans=LL(len-1)*LL(len+1)*LL(len)/2;
    height[1]=0;int k=0;
    for(int i=1;i<=len;i++)
    {
        int j=sa1[Rank[i]-1];
        if(k>0) k--;
        while(a[j+k]==a[i+k])k++;
        height[Rank[i]]=k;
    }
    int tail=0;
    for(int i=1;i<=len;i++)
    {
        while(tail!=0&&height[list[tail]]>height[i]) r[list[tail--]]=i-1;
        list[++tail]=i;
    }
    while(tail!=0) r[list[tail--]]=len;
    for(int i=len;i>=1;i--)
    {
        while(tail!=0&&height[list[tail]]>=height[i]) l[list[tail--]]=i+1;
        list[++tail]=i;
    }
    while(tail!=0) l[list[tail--]]=1;
    LL sum=0;
    for(int i=1;i<=len;i++) sum+=LL(i-l[i]+1)*LL(r[i]-i+1)*height[i];
    printf("%lld\n",ans-sum*2);
    return 0;
}

 

posted @ 2018-03-12 19:52  Star_Feel  阅读(170)  评论(0编辑  收藏  举报