bzoj 3238 [Ahoi2013]差异 后缀数组+单调栈

题目大意

给定长度为n<=500000的字符串S
\(T_i\)表示i开始的后缀
\(\sum_{1<=i<j<=n}len(T_i)+len(T_j)-2*lcp(T_i,T_j)\)

分析

后缀数组+单调栈

注意

long long

solution

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
typedef long long LL;
using namespace std;
const int M=1000007;

char s[M];
int n;
LL ans=0;

int sa[M],t[M];
int rk[M],f[M];
int sum[M],h[M];

void getsa(){
	int i,j,p,nw;
	memset(sum,0,sizeof(sum));
	for(i=1;i<=n;i++) sum[s[i]]++;
	for(i=1;i<=500;i++) sum[i]+=sum[i-1];
	for(i=n;i>0;i--) sa[sum[s[i]]--]=i;
	for(p=0,i=1;i<=n;i++) rk[sa[i]]=(s[sa[i]]!=s[sa[i-1]])?(++p):(p);
	for(j=1,nw=p;nw!=n;j<<=1,nw=p){
		memset(sum,0,sizeof(sum));
		memcpy(f,rk,sizeof(rk));
		for(p=0,i=n-j+1;i<=n;i++) t[++p]=i;
		for(i=1;i<=n;i++) if(sa[i]>j) t[++p]=sa[i]-j;
		for(i=1;i<=n;i++) sum[f[i]]++;
		for(i=1;i<=nw;i++) sum[i]+=sum[i-1];
		for(i=n;i>0;i--) sa[sum[f[t[i]]]--]=t[i];
		for(p=0,i=1;i<=n;i++) rk[sa[i]]=(f[sa[i]]!=f[sa[i-1]]||f[sa[i]+j]!=f[sa[i-1]+j])?(++p):(p);
	}
}

void geth(){
	int i,j,p=0;//p=0
	for(i=1;i<=n;i++){
		j=sa[rk[i]-1];
		for(;i+p<=n&&j+p<=n&&s[i+p]==s[j+p];p++);
		h[rk[i]]=p;
		if(p>0) p--;
	}
	h[1]=0;
}

struct node{
	int h,num;
	node(int hh=0,int nn=0){h=hh;num=nn;}
}st[M];
int tot=0;

LL solve(){
	LL res=0;
	LL cnt=0;
	int num;
	for(int i=1;i<=n;i++){
		num=1;
		cnt+=h[i];
		for(;tot>0&&st[tot].h>=h[i];tot--){
			num+=st[tot].num;
			cnt-=(LL)(st[tot].h-h[i])*st[tot].num;
		}
		st[++tot]=node(h[i],num);
		res+=cnt;
	}
	return res*2;
}

int main(){	
	scanf("%s",s+1);
	n=strlen(s+1);
	int i;
	for(i=1;i<=n;i++) ans+=(LL)(n-i+1)*(n-1);

	getsa();
	geth();
	LL tp=solve();

	printf("%lld\n",ans-tp);
	
	return 0;
}
posted @ 2017-02-20 07:42  _zwl  阅读(138)  评论(0编辑  收藏  举报