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;
}
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18118369,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。