题目大意:
对于给出的字符串(长度不超过1000) 求其不同的子串的个数
大致思路:
就是一个简单的height数组性质的利用,
如果每个子串我们用从位置j开始且长度为k来表示不同的子串的话则一个height[i]值说明表示
从位置sa[i - 1]开始的和sa[i]开始的长度为k <= height[i]的子串相同
所以用全部的子串数减去重复出现的即可, 具体见代码注释
代码如下:
Result : Accepted Memory : 3174 KB Time : 0 ms
/* * Author: Gatevin * Created Time: 2015/2/9 13:25:03 * File Name: Iris_Freyja.cpp */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> using namespace std; const double eps(1e-8); typedef long long lint; #define maxn 1233 /* * Doubling Algorithm 求后缀数组 */ int wa[maxn], wb[maxn], wv[maxn], Ws[maxn]; int 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 *x = wa, *y = wb, *t, i, j, p; for(int i = 0; i < m; i++) Ws[i] = 0; for(int i = 0; i < n; i++) Ws[x[i] = r[i]]++; for(int i = 1; i < m; i++) Ws[i] += Ws[i - 1]; for(int 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(t = x, x = y, y = t, 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++; } return; } int rank[maxn], height[maxn]; 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++); return; } char in[maxn]; int s[maxn]; int sa[maxn]; int main() { int t; scanf("%d", &t); while(t-->0) { scanf("%s", in); int n = strlen(in); for(int i = 0; i < n; i++) s[i] = in[i]; s[n] = 0; da(s, sa, n + 1, 130); calheight(s, sa, n); int ans = n*(n + 1)/2;//所有的子串数C[n][1] + C[n][2] /* * 利用height数组的性质 * 由于每个子串我们用从位置j开始且长度为k来表示不同的子串的话 * 则一个height[i]值表示 * 从位置sa[i - 1]开始的和sa[i]开始的长度为k <= height[i]的子串相同 * 所以将总数减去这个值即可 * 遍历一遍height数组去重就可以得到答案 */ for(int i = 1; i <= n; i++) ans -= height[i]; printf("%d\n", ans); } return 0; }