题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3518
题意: 给出一个字符串, 问其中有多少字串出现了两次以上(计算次数时不能彼此覆盖, 如 "aaaa" 中 "aa" 出现了两次而非三次).
思路: 后缀数组/字典树
后缀数组解法, 题目所求即使用后缀中出现两次以上的前缀数目. 可以枚举前缀长度, 将满足条件的前缀累进答案中. 在 SA 数组中, 具有相同前缀的后缀肯定是在一个连续块中的. 可以用 height 数组的性质来区分当前长度有哪些前缀块. 注意满足条件的前缀块中至少存在两个彼此不覆盖的前缀.
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define rank Rank 5 using namespace std; 6 7 const int MAXN = 1e4 + 10; 8 char str[MAXN]; 9 int SA[MAXN], rank[MAXN], height[MAXN], sum[MAXN], tp[MAXN], a[MAXN]; 10 11 bool cmp(int *f, int x, int y, int w){ 12 return f[x] == f[y] && f[x + w] == f[y + w]; 13 } 14 15 void get_SA(int *s, int n, int m){ 16 for(int i = 0; i < m; i++) sum[i] = 0; 17 for(int i = 0; i < n; i++) sum[rank[i] = s[i]]++; 18 for(int i = 1; i < m; i++) sum[i] += sum[i - 1]; 19 for(int i = n - 1; i >= 0; i--) SA[--sum[rank[i]]] = i; 20 for(int len = 1; len <= n; len <<= 1){ 21 int p = 0; 22 for(int i = n - len; i < n; i++) tp[p++] = i;//后面i个数没有第二关键字,即第二关键字为空,所以最小 23 for(int i = 0; i < n; i++){ 24 if(SA[i] >= len) tp[p++] = SA[i] - len; 25 } 26 //tp[i]存储按第二关键字排序第i的下标 27 //对第二关键字排序的结果再按第一关键字排序,和长度为1的情况类似 28 for(int i = 0; i < m; i++) sum[i] = 0; 29 for(int i = 0; i < n; i++) sum[rank[tp[i]]]++; 30 for(int i = 1; i < m; i++) sum[i] += sum[i - 1]; 31 for(int i = n - 1; i >= 0; i--) SA[--sum[rank[tp[i]]]] = tp[i]; 32 //根据SA和rank数组重新计算rank数组 33 swap(rank, tp);//交换后tp指向旧的rank数组 34 p = 1; 35 rank[SA[0]] = 0; 36 for(int i = 1; i < n; i++){ 37 rank[SA[i]] = cmp(tp, SA[i - 1], SA[i], len) ? p - 1 : p++; 38 } 39 if(p >= n) break; 40 m = p;//下次基数排序的最大值 41 } 42 //求height 43 int k = 0; 44 n--; 45 for(int i = 0; i <= n; i++) rank[SA[i]] = i; 46 for(int i = 0; i < n; i++){ 47 if(k) k--; 48 int j = SA[rank[i] - 1]; 49 while(s[i + k] == s[j + k]) k++; 50 height[rank[i]] = k; 51 } 52 } 53 54 int main(void){ 55 while(~scanf("%s", str)){ 56 if(str[0] == '#') break; 57 int len = strlen(str), sol = 0; 58 for(int i = 0; i < len; i++) a[i] = str[i]; 59 a[len] = 0; 60 get_SA(a, len + 1, 128); 61 for(int i = 1; i <= len / 2; i++){ 62 int l = MAXN, r = 0; 63 for(int j = 2; j <= len; j++){ 64 if(height[j] >= i){ 65 l = min(l, min(SA[j], SA[j - 1])); 66 r = max(r, max(SA[j], SA[j - 1])); 67 }else{ 68 if(r - l >= i) sol++; 69 l = MAXN; 70 r = 0; 71 } 72 } 73 if(r - l >= i) sol++; 74 } 75 printf("%d ", sol); 76 } 77 return 0; 78 }