BZOJ3238: [Ahoi2013]差异(后缀数组)

传送门

题意:

给定字符串S,求

1i<jnlen(suffix(i))+len(suffix(j))lcp(suffix(i),suffix(j))

题解:后缀数组
首先前面的一部分,每个后缀被枚举的次数为 n1 ,所以前部分的贡献为 (n1)n(n+1)2

对于后面一部分,考虑每一个height的贡献,这个height产生贡献时,当且仅当此height枚举两串排名之间的height最小值,所以可以用单调队列维护。

有一个比较麻烦的情况是前后有相同的值,一起计算就会重复。

如:{2,2,2,2,}。直接计算就是1*3+2*2+3*1=10,实际上只会枚举6次。
此时考虑左边相同的都要计算,右边不计算相同的。那么贡献就变成了1*1+2*1+3*1=6。(反过来只计算右边的也可以)。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int Maxn=5e5+50;
int n,m,INF,a[Maxn],b[Maxn],*rk=a,*sa2=b,h[Maxn],sa[Maxn],c[Maxn],bg[Maxn],ed[Maxn];
char ch[Maxn];
pii st[Maxn];

inline void Rsort()
{
    for(int i=1;i<=m;i++)c[i]=0;
    for(int i=1;i<=n;i++)c[rk[i]]++;
    for(int i=1;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i>=1;i--)sa[c[rk[sa2[i]]]--]=sa2[i];
}

inline void getsa()
{
    for(int i=1;i<=n;i++)rk[i]=ch[i],sa2[i]=i;
    m=123;Rsort();
    for(int w=1,p=0;p<n;m=p,w<<=1)
    {
        p=0;
        for(int i=n-w+1;i<=n;i++)sa2[++p]=i;
        for(int i=1;i<=n;i++)if(sa[i]>w)sa2[++p]=sa[i]-w;
        Rsort();swap(rk,sa2);rk[sa[1]]=p=1;
        for(int i=2;i<=n;i++)rk[sa[i]]=(sa2[sa[i]]!=sa2[sa[i-1]]||sa2[sa[i]+w]!=sa2[sa[i-1]+w])?(++p):p;
    }
    for(int k=0,i=1,j;i<=n;h[rk[i++]]=k)
        for(k?(k--):k,j=sa[rk[i]-1];ch[i+k]==ch[j+k];k++); 
}

int main()
{
    scanf("%s",ch+1);
    INF=n=strlen(ch+1);getsa();
    long long ans=1ll*(n-1)*(n)*(n+1)/2;
    for(int i=1,nowpos=0;i<=n;i++)
    {
        int cnt=0;
        while(nowpos&&st[nowpos].first>=h[i])cnt+=st[nowpos--].second;
        bg[i]=cnt;
        if(cnt)st[++nowpos]=make_pair(h[i],cnt);
        st[++nowpos]=make_pair(INF,1);
    }
    for(int i=n,nowpos=0;i>=1;i--)
    {
        int cnt=0;
        while(nowpos&&st[nowpos].first>h[i])cnt+=st[nowpos--].second;
        ed[i]=++cnt;
        st[++nowpos]=make_pair(h[i],cnt);
    }
    for(int i=1;i<=n;i++)ans-=2ll*h[i]*bg[i]*ed[i];
    printf("%lld",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值