SPOJ 694 || 705 Distinct Substrings ( 后缀数组 && 不同子串的个数 )

题意 : 对于给出的串,输出其不同长度的子串的种类数

分析 : 有一个事实就是每一个子串必定是某一个后缀的前缀,换句话说就是每一个后缀的的每一个前缀都代表着一个子串,那么如何在这么多子串or后缀的前缀中找出不同的并计数呢?思路就是所有的可能子串数 - 重复的子串数。首先我们容易得到一个长度为 len 的串的子串数为 len * ( len + 1) / 2。那如何知道重复的子串数呢?答案就是利用后缀数组去跑一遍 Height ,得到所有的最长公共前缀(LCP),这些最长公共前缀的值都存在了 Height 中,对于任意两个后缀的最长公共前缀长度实际就是重复出现的子串数,那么只要遍历一遍 Height 数组,用刚刚得出来的总子串数减去所有的 Height 值即可

#include<bits/stdc++.h>
using namespace std;
const int maxn = 50010;
int sa[maxn],s[maxn],wa[maxn], wb[maxn], Ws[maxn], wv[maxn];
int Rank[maxn], height[maxn];
bool cmp(int r[], int a, int b, int l){ return r[a] == r[b] && r[a+l] == r[b+l]; }
void da(int r[], int sa[], int n, int m)
{
    int i, j, p, *x = wa, *y = wb;
    for (i = 0; i < m; ++i) Ws[i] = 0;
    for (i = 0; i < n; ++i) Ws[x[i]=r[i]]++;
    for (i = 1; i < m; ++i) Ws[i] += Ws[i-1];
    for (i = n-1; i >= 0; --i) sa[--Ws[x[i]]] = i;
    for (j = 1, p = 1; p < n; j *= 2, m = p)
    {
        for (p = 0, i = n - j; i < n; ++i) y[p++] = i;
        for (i = 0; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j;
        for (i = 0; i < n; ++i) wv[i] = x[y[i]];
        for (i = 0; i < m; ++i) Ws[i] = 0;
        for (i = 0; i < n; ++i) Ws[wv[i]]++;
        for (i = 1; i < m; ++i) Ws[i] += Ws[i-1];
        for (i = n-1; i >= 0; --i) sa[--Ws[wv[i]]] = y[i];
        for (std::swap(x, y), p = 1, x[sa[0]] = 0, i = 1; i < n; ++i)
            x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p-1 : p++;
    }
}
void calheight(int r[], int sa[], int n)
{
    int i, j, k = 0;
    for (i = 1; i <= n; ++i) Rank[sa[i]] = i;
    for (i = 0; i < n; height[Rank[i++]] = k)
        for (k?k--:0, j = sa[Rank[i]-1]; r[i+k] == r[j+k]; k++);
}

char SS[maxn];
int S[maxn];
int main(void)
{
    int nCase;
    scanf("%d", &nCase);
    while(nCase--){
        scanf("%s", SS);
        int len = strlen(SS);
        for(int i=0; i<len; i++) S[i] = (int)SS[i];
        S[len] = 0;

        da(S, sa, len+1, 128);
        calheight(S, sa, len);

        long long tmp = len;
        long long ans = (tmp * (tmp+1)) / 2;
        for(int i=2; i<=len; i++) ans -= height[i];//or ans += len - i - height[Rank[i]];
        printf("%lld
", ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/qwertiLH/p/7868700.html