题意:
给出一个字符串,分别求出前1~n位所含的不同的字符串个数
n<=100000 n <= 100000
Solution:
看这道题的时候感觉到有后缀排序的那么点意思,然而只是感觉到而已…
正解太TM神了
考虑对于一个字符串,他所产生的本质不同的字符串个数为所有后缀的长度和减去height数组的和
那么我们如何动态的求呢?我们每次是在字符串后面增加一个字符,这样所有的后缀都会改变,非常不好维护,所以我们就翻转整个字符串,每次在字符串前面加字符,这样旧的后缀就不会改变,只会增加一个新的后缀
那么我们的问题就变成了:增加一个字符串,求他与前面的所有字符串中最长的LCP是多少
考虑先求出整个字符串的后缀数组,那么我们每次加入一个字符串,求得的最长的LCP一定是在这个字符串前后与它rank最近的字符串中产生,求这个字符串的的前后最近可以用平衡树或线段树来维护, 求LCP可以通过height数组RMQ求出
总复杂度 O(nlogn) O ( n log n )
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=200010;
int a[N],n,p;
int lsh[N],cnt;
int SA[N],rk[N],tp[N],m,tax[N],h[N],f[N][20],lg[N],mi[20];
long long ans;
struct tree{
int l,r,maxn,minn;
}tr[4*N];
void Jsort()
{
for (int i=1;i<=m;i++) tax[i]=0;
for (int i=1;i<=n;i++) tax[rk[tp[i]]]++;
for (int i=2;i<=m;i++) tax[i]+=tax[i-1];
for (int i=n;i>=1;i--) SA[tax[rk[tp[i]]]--]=tp[i];
}
bool cmp(int tp[],int x,int y,int w)
{
return (tp[x]==tp[y]&&tp[x+w]==tp[y+w]);
}
int queryh(int l,int r)
{
int len=r-l+1;
return min(f[l][lg[len]],f[r-mi[lg[len]]+1][lg[len]]);
}
void build(int i,int l,int r)
{
tr[i].l=l;tr[i].r=r;tr[i].minn=1e9;
if (l==r) return;
int mid=l+r>>1;
build(i<<1,l,mid);build(i<<1|1,mid+1,r);
}
void modify(int i,int pos,int x)
{
int L=tr[i].l,R=tr[i].r;
if (L==R) {
tr[i].maxn=x;tr[i].minn=x;return;}
int mid=L+R>>1;
if (x<=mid) modify(i<<1,pos,x);
else modify(i<<1|1,pos,x);
tr[i].maxn=max(tr[i<<1].maxn,tr[i<<1|1].maxn);
tr[i].minn=min(tr[i<<1].minn==0?1e9:tr[i<<1].minn,tr[i<<1|1].minn==0?1e9:tr[i<<1|1].minn);
}
int querymax(int i,int l,int r)
{
if (l>r) return 0;
int L=tr[i].l,R=tr[i].r;
if (L>r||l>R) return 0;
if (l<=L&&R<=r) return tr[i].maxn;
return max(querymax(i<<1,l,r),querymax(i<<1|1,l,r));
}
int querymin(int i,int l,int r)
{
if (l>r) return 0;
int L=tr[i].l,R=tr[i].r;
if (L>r||l>R) return 1e9;
if (l<=L&&R<=r) return tr[i].minn;
return min(querymin(i<<1,l,r),querymin(i<<1|1,l,r));
}
int main()
{
scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&a[i]),lsh[++cnt]=a[i];
sort(lsh+1,lsh+1+cnt);cnt=unique(lsh+1,lsh+1+cnt)-lsh-1;
for (int i=1;i<=n;i++) a[i]=lower_bound(lsh+1,lsh+1+cnt,a[i])-lsh;
int l=1,r=n;
for (;l<r;l++,r--) swap(a[l],a[r]);
m=cnt;
for (int i=1;i<=n;i++) tp[i]=i,rk[i]=a[i];
Jsort();
for (int w=1;p<n;w+=w,m=p)
{
p=0;
for (int i=n-w+1;i<=n;i++) tp[++p]=i;
for (int i=1;i<=n;i++) if (SA[i]>w) tp[++p]=SA[i]-w;
Jsort();swap(tp,rk);
p=1;rk[SA[1]]=p;
for (int i=2;i<=n;i++) if (cmp(tp,SA[i],SA[i-1],w)) rk[SA[i]]=p;else rk[SA[i]]=++p;
}
int k=0;
build(1,1,n);
for (int i=1;i<=n;i++)
{
if (rk[i]==1) {h[1]=0;continue;}
if (k) k--;
int j=SA[rk[i]-1];
while (a[i+k]==a[j+k]&&i+k<=n&&j+k<=n) k++;
h[rk[i]]=k;
}
mi[0]=1;lg[1]=0;for (int i=1;mi[i-1]<=n;i++) mi[i]=mi[i-1]*2,lg[mi[i]]=i;
for (int i=2;i<=n;i++) if (!lg[i]) lg[i]=lg[i-1];
for (int i=1;i<=n;i++) f[i][0]=h[i];
for (int i=1;i<=19;i++)
for (int j=1;j<=n;j++)
f[j][i]=min(f[j][i-1],f[min(n,j+(1<<i-1))][i-1]);
for (int i=n;i>=1;i--)
{
int pre=querymax(1,1,rk[i]-1),nxt=querymin(1,rk[i]+1,n);
int tmp=0;
if (pre) tmp=max(queryh(pre+1,rk[i]),tmp);
if (nxt<=n) tmp=max(queryh(rk[i]+1,nxt),tmp);
ans+=n-i+1-tmp;
modify(1,rk[i],rk[i]);
printf("%lld\n",ans);
}
}