AT_s8pc_2_e 部分文字列 题解

题目传送门

前置知识

后缀数组简介

解法

对于一个后缀 \(s_{sa_{i} \sim n}\),它产生了 \(n-sa_{i}+1\) 个前缀,其长度和为 \(\frac{(n-sa_{i}+1)(n-sa_{i}+2)}{2}\);和 \(s_{sa_{i-1} \sim n}\) 相比产生了 \(height_{i}\) 个相同的前缀,其长度和为 \(\frac{height_{i}(height_{i}+1)}{2}\)

最终,有 $\sum\limits_{i=1}{n}\frac{(n-sa_{i}+1)(n-sa_{i}+2)}{2}-\sum\limits_{i=1}\frac{height_{i}(height_{i}+1)}{2} $ 即为所求,化简完得到 \(\frac{1}{2} \times (\frac{n(n+1)(2n+1)}{6}+\frac{n(n+1)}{2})-\sum\limits_{i=1}^{n}\frac{height_{i}(height_{i}+1)}{2}\)

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define ull unsigned long long
#define sort stable_sort 
#define endl '\n'
ll sa[100010],rk[200010],oldrk[200010],id[100010],cnt[100010],key[100010],height[100010],a[100010],b[100010],fminn[100010][20];
char s[100010];
ll val(char x)
{
	return (ll)x;
}
void counting_sort(ll n,ll m)
{
	memset(cnt,0,sizeof(cnt));
	for(ll i=1;i<=n;i++)
	{
		cnt[key[i]]++;
	}
	for(ll i=1;i<=m;i++)
	{
		cnt[i]+=cnt[i-1];
	}
	for(ll i=n;i>=1;i--)
	{
		sa[cnt[key[i]]]=id[i];
		cnt[key[i]]--;
	}
}
void init_sa(char s[],ll len)
{
	ll m=127,tot=0,num=0,i,j,w;
	for(i=1;i<=len;i++)
	{
		rk[i]=val(s[i]);
		id[i]=i;
		key[i]=rk[id[i]];
	}
	counting_sort(len,m);
	for(w=1;tot!=len;w<<=1,m=tot)
	{
		num=0;
		for(i=len;i>=len-w+1;i--)
		{
			num++;
			id[num]=i;
		}
		for(i=1;i<=len;i++)
		{
			if(sa[i]>w)
			{
				num++;
				id[num]=sa[i]-w;
			}
		}
		for(i=1;i<=len;i++)
		{
			key[i]=rk[id[i]];
		}
		counting_sort(len,m);
		for(i=1;i<=len;i++)
		{
			oldrk[i]=rk[i];
		}
		tot=0;
		for(i=1;i<=len;i++)
		{
			tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]);
			rk[sa[i]]=tot;
		}
	}
	for(i=1,j=0;i<=len;i++)
	{
		j-=(j>=1);
		while(s[i+j]==s[sa[rk[i]-1]+j])
		{
			j++;
		}
		height[rk[i]]=j;
	}
}
int main()
{
	ll n,sum=0,i;
	scanf("%s",s+1);
    n=strlen(s+1);
	init_sa(s,n);
	for(i=1;i<=n;i++)
	{
		sum+=height[i]*(height[i]+1)/2;
	}
	cout<<(n*(n+1)*(2*n+1)/6+n*(n+1)/2)/2-sum<<endl;
	return 0;
}
posted @ 2024-04-07 09:08  hzoi_Shadow  阅读(5)  评论(0编辑  收藏  举报
扩大
缩小