【bzoj3238】【AHOI2013】【差异】【后缀数组+单调栈】

Description

Input

一行,一个字符串S

Output

 

一行,一个整数,表示所求值

Sample Input

cacao

Sample Output


54

HINT



2<=N<=500000,S由小写英文字母组成

题解:

          把式子拆开之后可以发现就是所有后缀长度的n-1倍减去任两个后缀的lcp的长度的两倍.

          第一部分直接算即可,第二部分可以用后缀数组.

          求出height数组之后对于height[i]找出他前面第一个小于等于它的位置和后面第一个小于它的位置.

          这之间的i前面的和i后面的任意两个后缀的lcp长度都是height[i];

          扫一遍统计一下即可.

代码:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 500010
using namespace std;
int n,sa[N],s[N],t1[N],t2[N],rank[N],c[N],h[N],after[N],pre[N],top,st[N];
long long ans;
char ch[N];
bool cmp(int *y,int p,int q,int k){
  int a=(p+k)>=n?-1:y[p+k];
  int b=(q+k)>=n?-1:y[q+k];
  return a==b&&y[p]==y[q];
}
void build_sa(int m){
  int *x=t1,*y=t2;
  for (int i=0;i<m;i++) c[i]=0;
  for (int i=0;i<n;i++) c[x[i]=s[i]]++;
  for (int i=1;i<m;i++) c[i]+=c[i-1];
  for (int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
  for (int k=1;k<=n;k<<=1){
    int p=0;
    for (int i=n-k;i<n;i++) y[p++]=i;
    for (int i=0;i<n;i++) if (sa[i]>=k) y[p++]=sa[i]-k;
    for (int i=0;i<m;i++) c[i]=0;
    for (int i=0;i<n;i++) c[x[y[i]]]++;
    for (int i=1;i<m;i++) c[i]+=c[i-1];
    for (int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    swap(x,y);
    p=1;x[sa[0]]=0;
    for (int i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
    if (p>=n) break;
    m=p;
  } 
}
void geth(){
  int k(0);
  for (int i=0;i<n;i++) rank[sa[i]]=i;
  for (int i=0;i<n;i++){
    if (!rank[i]) continue;
    if (k) k--;int j=sa[rank[i]-1];
    while (s[j+k]==s[i+k]) k++;
    h[rank[i]]=k;
  }
  //for (int i=0;i<n;i++) cout<<h[i]<<' ';
  //cout<<endl;
}
int main(){
  scanf("%s",ch);n=strlen(ch);
  for (int i=0;i<n;i++) s[i]=ch[i]-'a'+1;
  build_sa(27);geth();
  for (int i=1;i<=n;i++) ans+=i;
  ans=ans*(long long)(n-1);
  top=0;pre[1]=0;st[++top]=1;
  for (int i=2;i<=n-1;i++){
    while (top&&h[st[top]]>h[i]) top--;
    pre[i]=st[top];
    st[++top]=i;
  }
  top=0;after[n-1]=n;st[++top]=n-1;st[0]=n;
  for (int i=n-1;i>=1;i--){
    while (top&&h[st[top]]>=h[i]) top--;
    after[i]=st[top];
    st[++top]=i;
  }
 // for (int i=1;i<n;i++) cout<<pre[i]<<' '<<after[i]<<endl;
  for (int i=1;i<n;i++)
     ans=ans-(long long)(after[i]-i)*(i-pre[i])*h[i]*2; 
  cout<<ans<<endl;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值