hdu3518(后缀数组)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3518

 

题意: 给出一个字符串, 问其中有多少字串出现了两次以上(计算次数时不能彼此覆盖, 如 "aaaa"  中 "aa" 出现了两次而非三次).

 

思路: 后缀数组/字典树

后缀数组解法, 题目所求即使用后缀中出现两次以上的前缀数目. 可以枚举前缀长度, 将满足条件的前缀累进答案中. 在 SA 数组中, 具有相同前缀的后缀肯定是在一个连续块中的. 可以用 height 数组的性质来区分当前长度有哪些前缀块. 注意满足条件的前缀块中至少存在两个彼此不覆盖的前缀.

 

代码:

 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\n", sol);
76     }
77     return 0;
78 }
View Code

 

posted @ 2017-09-01 17:15  geloutingyu  阅读(364)  评论(0编辑  收藏  举报