SPOJ 694 || 705 Distinct Substrings ( 后缀数组 && 不同子串的个数 )
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\n", ans); } return 0; }
以上是关于SPOJ 694 || 705 Distinct Substrings ( 后缀数组 && 不同子串的个数 )的主要内容,如果未能解决你的问题,请参考以下文章
SPOJ694/DISUBSTR???Distinct Substrings????????????
spoj694 DISUBSTR - Distinct Substrings