P2408 不同子串个数(SA-LCP)

冷暖自知 提交于 2020-08-14 03:12:18

P2408 不同子串个数(SA-LCP)

传送门

经典的 S A − L C P SA-LCP SALCP题目。

显然所有子串数目为 n ( n + 1 ) 2 \dfrac{n(n+1)}{2} 2n(n+1)

因此我们只需知道重复的子串有多少个。

根据 L C P LCP LCP我们知道利用 L C P LCP LCP求出的 h e i g h t [ i ] = L C P ( i , i − 1 ) , 即 排 名 第 i 名 和 第 i − 1 名 的 最 长 公 共 前 缀 height[i]=LCP(i,i-1),即排名第i名和第i-1名的最长公共前缀 height[i]=LCP(i,i1)ii1,他们的长度即是重复的子串个数,这样求的正确性是因为要找到重复的子串,首先两个子串下标必须不一样,且长度一样,字母完全一样,所以根据排名第 i i i名和第 i − 1 i-1 i1名是字典序最接近的可以知道 h e i g h t [ i ] height[i] height[i]求代表重复子串个数,求和便可得到重复子串总数。

所以 a n s = n ( n + 1 ) 2 − ∑ i = 1 n h e i g h t [ i ] ans=\dfrac{n(n+1)}{2}-\sum\limits_{i=1}^n height[i] ans=2n(n+1)i=1nheight[i]

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=1e6+5;
char s[N];
int n,sz,sa[N],rk[N],tp[N],b[N],ht[N];
typedef long long ll; 
void Qsort(){	//基数排序. 
	for(int i=0;i<=sz;i++) b[i]=0;
	for(int i=1;i<=n;i++) b[rk[i]]++; 
	for(int i=1;i<=sz;i++) b[i]+=b[i-1];
	for(int i=n;i>=1;i--) sa[b[rk[tp[i]]]--]=tp[i];
}
void SA(){
	sz=80; 
	for(int i=1;i<=n;i++) rk[i]=s[i]-'0'+1,tp[i]=i;
	Qsort();
	for(int w=1,id=0;id<n;sz=id,w<<=1){		
		id=0;
		for(int i=n-w+1;i<=n;i++) tp[++id]=i;
		for(int i=1;i<=n;i++) if(sa[i]>w) tp[++id]=sa[i]-w;
		Qsort(); 
		swap(rk,tp); 
		rk[sa[1]]=id=1;
		for(int i=2;i<=n;i++) 
			rk[sa[i]]= (tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])?id:++id;			 
	}
}
void GetHight(){		//求height 
	int k=0;
	for(int i=1;i<=n;i++){
		if(k) k--;
		int j=sa[rk[i]-1];
		while(s[i+k]==s[j+k]) k++;
		ht[rk[i]]=k;
	}
} 
int main(){
	scanf("%d%s",&n,s+1);
	SA();
	GetHight();
	ll ans=1LL*n*(n+1)/2;
	for(int i=1;i<=n;i++) ans-=ht[i];
	printf("%lld\n",ans);
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!