SPOJ 694 求一個字串有多少子串 字尾陣列

life4711發表於2014-08-04

http://www.spoj.com/problems/DISUBSTR/


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 <= 1000

Output

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

Example

Sample Input:
2
CCCCC
ABABA

Sample Output:
5
9

Explanation for the testcase with string ABABA: 
len=1 : A,B
len=2 : AB,BA
len=3 : ABA,BAB
len=4 : ABAB,BABA
len=5 : ABABA
Thus, total number of distinct substrings is 9.


解題思路:一個字串中的所有子串都必然是它的字尾的字首。(這句話稍微有點繞...)對於每一個sa[i]字尾,它的起始位置sa[i],那麼它最多能得到該字尾長度個子串(n-sa[i]個),而其中有height[i]個是與前一個字尾相同的,所以它能產生的實際字尾個數便是n-sa[i]-height[i]。遍歷一次所有的字尾,將它產生的字尾數加起來便是答案。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=211111;
/******************************************************************
**  字尾陣列 Suffix Array
**  INIT:solver.call_fun(char* s);
**  CALL: solver.lcp(int i,int j); //字尾i與字尾j的最長公共字首
**  SP_USE: solver.LCS(char *s1,char* s2); //最長公共字串
******************************************************************/
struct SuffixArray
{
    int r[maxn];
    int sa[maxn],rank[maxn],height[maxn];
    int t[maxn],t2[maxn],c[maxn],n;
    int m;//模板長度
    void init(char* s)
    {
        n=strlen(s);
        for (int i=0; i<n; i++) r[i]=int(s[i]);
        m=300;
    }
    int cmp(int *r,int a,int b,int l)
    {
        return r[a]==r[b]&&r[a+l]==r[b+l];
    }
    /**
    字元要先轉化為正整數
    待排序的字串放在r[]陣列中,從r[0]到r[n-1],長度為n,且最大值小於m。
    所有的r[i]都大於0,r[n]無意義演算法中置0
    函式結束後,結果放在sa[]陣列中(名次從1..n),從sa[1]到sa[n]。s[0]無意義
    **/
    void build_sa()
    {
        int i,k,p,*x=t,*y=t2;
        r[n++]=0;
        for (i=0; i<m; i++) c[i]=0;
        for (i=0; i<n; i++) c[x[i]=r[i]]++;
        for (i=1; i<m; i++) c[i]+=c[i-1];
        for (i=n-1; i>=0; i--) sa[--c[x[i]]]=i;
        for (k=1,p=1; k<n; k*=2,m=p)
        {
            for (p=0,i=n-k; i<n; i++) y[p++]=i;
            for (i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;
            for (i=0; i<m; i++) c[i]=0;
            for (i=0; i<n; i++) c[x[y[i]]]++;
            for (i=1; i<m; i++) c[i]+=c[i-1];
            for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]]=y[i];
            swap(x,y);
            p=1;
            x[sa[0]]=0;
            for (i=1; i<n; i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++;
        }
        n--;
    }
    /**
    height[2..n]:height[i]儲存的是lcp(sa[i],sa[i-1])
    rank[0..n-1]:rank[i]儲存的是原串中suffix[i]的名次
    **/
    void getHeight()
    {
        int i,j,k=0;
        for (i=1; i<=n; i++) rank[sa[i]]=i;
        for (i=0; i<n; i++)
        {
            if (k) k--;
            j=sa[rank[i]-1];
            while (r[i+k]==r[j+k]) k++;
            height[rank[i]]=k;
        }
    }
    int d[maxn][20];
    //元素從1編號到n
    void RMQ_init(int A[],int n)
    {
        for (int i=1; i<=n; i++) d[i][0]=A[i];
        for (int j=1; (1<<j)<=n; j++)
            for (int i=1; i+j-1<=n; i++)
                d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
    }
    int RMQ(int L,int R)
    {
        int k=0;
        L=rank[L];
        R=rank[R];
        if(L>R) swap(L,R);
        L++;
        while ((1<<(k+1))<=R-L+1) k++;
        return min(d[L][k],d[R-(1<<k)+1][k]);
    }
    void LCP_init()
    {
        RMQ_init(height,n);
    }
    int lcp(int i,int j)
    {
        if (rank[i]>rank[j]) swap(i,j);
        return RMQ(rank[i]+1,rank[j]);
    }
    void call_fun(char* s)
    {
        init(s);//初始化字尾陣列
        build_sa();//構造字尾陣列sa
        getHeight();//計算height與rank
        LCP_init();//初始化RMQ
    }
    int so(int n)
    {
        int ans=0;
        for(int i=1; i<=n; i++)
        {
            ans+=(n-sa[i]-height[i]);
        }
        return ans;
    }
} solver;
int main()
{
    char s[1005];
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s);
        int n=strlen(s);
        solver.call_fun(s);
        int ans=solver.so(n);
        printf("%d\n",ans);
    }
    return 0;
}


相關文章