【不同子串個數】
阿新 • • 發佈:2019-01-02
這是\(sa\)的經典題目了
我們都知道答案就是
\[\sum_{i=1}^nn+1-sa[i]-het[i]\]
我們嘗試理解一下這個東西
首先\(n+1-sa[i]\)表示的是排名為\(i\)的這個字尾能形成的子串個數是多少個,也就是從\(sa[i]\)位置開始的子串
之後減掉\(het[i]\)表示減掉的是和排名為\(i-1\)的字尾相同的子串
還有一個巧妙的性質,就是我們這樣得到的本質不同的子串都是有序的
非常顯然因為\(sa\)是有序的
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define re register #define LL long long #define maxn 500005 #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) inline int read() { re char c=getchar();int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x; } char S[maxn]; int sa[maxn],rk[maxn],het[maxn],tp[maxn],tax[maxn]; int n,m; LL ans; inline void qsort() { for(re int i=0;i<=m;i++) tax[i]=0; for(re int i=1;i<=n;i++) tax[rk[i]]++; for(re int i=1;i<=m;i++) tax[i]+=tax[i-1]; for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i]; } int main() { scanf("%d",&n),scanf("%s",S+1);m=255; for(re int i=1;i<=n;i++) rk[i]=S[i],tp[i]=i; qsort(); for(re int w=1,p=0;p<n;m=p,w<<=1) { p=0; for(re int i=1;i<=w;i++) tp[++p]=n-w+i; for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w; qsort(); for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]); rk[sa[1]]=p=1; for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p; } int k=0; for(re int i=1;i<=n;i++) { if(k) --k; int j=sa[rk[i]-1]; while(S[i+k]==S[j+k]) ++k; het[rk[i]]=k; } for(re int i=1;i<=n;i++) ans+=n+1-sa[i]-het[i]; printf("%lld\n",ans); return 0; }