1. 程式人生 > >洛谷P2408 不同子串個數 後綴數組_Height數組

洛谷P2408 不同子串個數 後綴數組_Height數組

fin fix ++i for -h += spa _array 我們


## 題目描述:

給你一個長為 $N$ $(N<=10^5)$ 的字符串,求不同的子串的個數
我們定義兩個子串不同,當且僅當有這兩個子串長度不一樣 或者長度一樣且有任意一位不一樣。子串的定義:原字符串中連續的一段字符組成的字符串


很妙的一道題,考察了對 $Height$ 數組的理解。

$1.$首先,不難發現任意子串都可以被字符串中後綴串的前綴表達出來

$2.$我們知道, $Height[i]$ 被定義為排名為 $i$ 的後綴串與排名為 $i-1$ 的後綴串的 $LCP$.

而與排名為 $i$ 得後綴串 $LCP$ 值最大的字符串必定是排名為 $i-1$ 的後綴串,他們的 $LCP$

值恰好就是 $Height$ 數組的值,即$Height[i]$.

考慮向後綴串集合中新加入一個後綴串 $sa[k]$, 共會產生 $n-sa[k]+1$ 個前綴串,但是有一些

前綴串在先前就已經會被計算到,會被計算到的前綴部分的最大值是 $Height[k]$,直接減去

$Height[k]$ 即可. 即貢獻為 $n-sa[k]+1-Height[k]$.

Code:

#include <cstdio>
#include <algorithm>
#include <cstring>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 1000000
using namespace std;
char str[maxn];
int tr[maxn],rk[maxn],sa[maxn],arr[maxn],c[maxn],height[maxn]; 
int n,m;
struct Suffix_Array
{
    void qsort()
    {
        for(int i=0;i<=m;++i) c[i]=0;
        for(int i=1;i<=n;++i) ++c[rk[tr[i]]];
        for(int i=1;i<=m;++i) c[i]+=c[i-1];
        for(int i=n;i>=1;--i) sa[c[rk[tr[i]]]--]=tr[i]; 
    }
    void build()
    {
        for(int i=1;i<=n;++i) rk[i]=arr[i],tr[i]=i; 
        qsort(); 
        for(int k=1;k<=n;k<<=1)
        {
            int num=0;
            for(int i=n-k+1;i<=n;++i) tr[++num]=i;
            for(int i=1;i<=n;++i) if(sa[i]>k) tr[++num]=sa[i]-k;
            qsort();
            swap(rk,tr);
            rk[sa[1]]=1; 
            num=1;
            for(int i=2;i<=n;++i)
                rk[sa[i]]=(tr[sa[i]]==tr[sa[i-1]]&&tr[sa[i]+k]==tr[sa[i-1]+k])?num:++num;
            if(num>=n) break;
            m=num; 
        }
    }
    void get_height()
    {
        int k=0;
        for(int i=1;i<=n;++i) rk[sa[i]]=i;
        for(int i=1;i<=n;++i){
            if(k) --k;
            int j=sa[rk[i]-1]; 
            while(arr[i+k]==arr[j+k]) ++k;
            height[rk[i]]=k; 
        }
    }
}T;
int main()
{
    //setIO("input"); 
    scanf("%d",&n);
    scanf("%s",str),m=120;
    for(int i=1;i<=n;++i) arr[i]=str[i-1]-‘0‘;
    T.build();
    T.get_height();
    long long ans=0;
    for(int i=1;i<=n;++i)
        ans+=(long long) (n-sa[i]+1-height[i]); 
    printf("%lld",ans);
    return 0; 
}

  

洛谷P2408 不同子串個數 後綴數組_Height數組