[BZOJ3238]差异

反串的sam的parent tree就是后缀树...而且每个节点的$v$就是后缀树每个节点代表的字符串长度

因为在反串的某个子串往前延伸一个字符对应着sam的parent tree中的儿子,也对应着原串中后缀树的分岔

所以用这种方式构建出后缀树,那么后缀的lcp就是lca了,直接在树上统计答案即可,对每个节点统计有多少个节点对的lca是这个节点即可

#include<stdio.h>
#include<string.h>
typedef long long ll;
struct sam{
	int ch[26],fa,v;
}t[1000010];
char s[500010];
int c[1000010],sa[1000010],siz[1000010];
int M=1,las=1;
void extend(int c){
	int p=las,np=++M,q,nq;
	t[np].v=t[p].v+1;
	siz[np]=1;
	while(p&&t[p].ch[c]==0){
		t[p].ch[c]=np;
		p=t[p].fa;
	}
	if(!p)
		t[np].fa=1;
	else{
		q=t[p].ch[c];
		if(t[q].v==t[p].v+1)
			t[np].fa=q;
		else{
			nq=++M;
			t[nq]=t[q];
			t[nq].v=t[p].v+1;
			t[np].fa=t[q].fa=nq;
			while(p&&t[p].ch[c]==q){
				t[p].ch[c]=nq;
				p=t[p].fa;
			}
		}
	}
	las=np;
}
void sort(){
	int i;
	for(i=1;i<=M;i++)c[t[i].v]++;
	for(i=1;i<=M;i++)c[i]+=c[i-1];
	for(i=M;i>0;i--)sa[c[t[i].v]--]=i;
}
int main(){
	int n,i,x;
	ll ans;
	scanf("%s",s);
	n=strlen(s);
	for(i=n-1;i>=0;i--)extend(s[i]-'a');
	sort();
	ans=(ll)n*(n-1)*(n+1)/2;
	for(i=M;i>1;i--){
		x=sa[i];
		ans-=(ll)siz[x]*siz[t[x].fa]*t[t[x].fa].v*2;
		siz[t[x].fa]+=siz[x];
	}
	printf("%lld",ans);
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/9215437.html