題意:
給出一個字符串,分別求出前1~n位所含的不同的字符串個數
Solution:
看這道題的時候感覺到有後綴排序的那麼點意思,然而只是感覺到而已…
正解太TM神了
考慮對於一個字符串,他所產生的本質不同的字符串個數爲所有後綴的長度和減去height數組的和
那麼我們如何動態的求呢?我們每次是在字符串後面增加一個字符,這樣所有的後綴都會改變,非常不好維護,所以我們就翻轉整個字符串,每次在字符串前面加字符,這樣舊的後綴就不會改變,只會增加一個新的後綴
那麼我們的問題就變成了:增加一個字符串,求他與前面的所有字符串中最長的LCP是多少
考慮先求出整個字符串的後綴數組,那麼我們每次加入一個字符串,求得的最長的LCP一定是在這個字符串前後與它rank最近的字符串中產生,求這個字符串的的前後最近可以用平衡樹或線段樹來維護, 求LCP可以通過height數組RMQ求出
總複雜度
代碼:
#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);
}
}