天天看點

SPOJ SUBST1 - New Distinct Substrings (字尾數組)

Description

Given a string, we need to find the total number of its distinct substrings.

Input

T - number of test cases. T<=20; Each test case consists of one string, whose length is <= 50000

Output

For each test case output one number saying the number of distinct substrings.

Example Input

2
CCCCC
ABABA      

Example Output

5
9      

題意

給定一個字元串,求其不相同的子串數目。

思路

每個子串一定是某個字尾的字首,那麼原問題便等價于求所有字尾之間有多少個不相同的字首,可以發現,每增加一個字尾貢獻 len−SA[i]+1 個字首,但這些子串中有重複計算,重複的個數為 Height[i]

AC 代碼

#include<bits/stdc++.h>
#define rank rankk
using namespace std;

const int MAXN = 2e5+10;

char ch[MAXN], All[MAXN];
int SA[MAXN], rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m;
char str[MAXN];
//rank[i] 第i個字尾的排名; SA[i] 排名為i的字尾位置; Height[i] 排名為i的字尾與排名為(i-1)的字尾的LCP
//tax[i] 計數排序輔助數組; tp[i] rank的輔助數組(計數排序中的第二關鍵字),與SA意義一樣。
//a為原串
void RSort()
{
    //rank第一關鍵字,tp第二關鍵字。
    for (int i = 0; i <= m; i ++) tax[i] = 0;
    for (int i = 1; i <= n; i ++) tax[rank[tp[i]]] ++;
    for (int i = 1; i <= m; i ++) tax[i] += tax[i-1];
    for (int i = n; i >= 1; i --) SA[tax[rank[tp[i]]] --] = tp[i]; //確定滿足第一關鍵字的同時,再滿足第二關鍵字的要求
} //計數排序,把新的二進制組排序。

int cmp(int *f, int x, int y, int w)
{
    return f[x] == f[y] && f[x + w] == f[y + w];
}
//通過二進制組兩個下标的比較,确定兩個子串是否相同

void Suffix()
{
    //SA
    for (int i = 1; i <= n; i ++) rank[i] = a[i], tp[i] = i;
    m = 127,RSort();  //一開始是以單個字元為機關,是以(m = 127)

    for (int w = 1, p = 1, i; p < n; w += w, m = p)   //把子串長度翻倍,更新rank
    {

        //w 目前一個子串的長度; m 目前離散後的排名種類數
        //目前的tp(第二關鍵字)可直接由上一次的SA的得到
        for (p = 0, i = n - w + 1; i <= n; i ++) tp[++ p] = i; //長度越界,第二關鍵字為0
        for (i = 1; i <= n; i ++) if (SA[i] > w) tp[++ p] = SA[i] - w;

        //更新SA值,并用tp暫時存下上一輪的rank(用于cmp比較)
        RSort(), swap(rank, tp), rank[SA[1]] = p = 1;

        //用已經完成的SA來更新與它互逆的rank,并離散rank
        for (i = 2; i <= n; i ++) rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;
    }
    //離散:把相等的字元串的rank設為相同。
    //LCP
    int j, k = 0;
    for(int i = 1; i <= n; Height[rank[i ++]] = k)
        for( k = k ? k - 1 : k, j = SA[rank[i] - 1]; a[i + k] == a[j + k]; ++ k);
    //這個知道原理後就比較好了解程式
}

void solve()
{
    n = strlen(str);
    for(int i=0; i<n; i++)
        a[i+1]=str[i];
    Suffix();
    int ans=0;
    for(int i=1; i<=n; i++)
        ans+=n-SA[i]+1-Height[i];
    cout<<ans<<endl;
}

int main()
{
    ios::sync_with_stdio(false);
    int T;
    cin>>T;
    while(T--)
    {
        cin>>str;
        solve();
    }
    return 0;
}