后缀数组的一个简单应用,即计算在字符串中不可重叠的的最长重复子串的长度。
可以将问题转化为判定性问题:是否存在两个长度为k的子串是相同且不重叠的。利用height数组进行计算。把排序后的后缀数组分成若干组,其中每组后缀的height都不小于某一个下限。有希望成为最长公共前缀长度不小于k的两个后缀一定在同一个组里。然后只需判断目前为止后缀的SA值的最大值和最小值之差是否不小于k。如果是,则说明存在两个后缀,其公共前缀的长度不小于k且互不重叠。
依据height值的下限对后缀进行分组的方法,在字符串处理中很常用。
代码中有些细节根据具体题目。后缀数组部分为模板。此模板与普遍的模板不同,比普通的模板更易于理解。
总复杂度O(n*log n )
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 using namespace std; 6 #define maxn 20010 7 int n; 8 struct node 9 { 10 int now, next; 11 } d[maxn]; 12 int val[maxn][2], c[maxn], Rank[maxn], SA[maxn], pos[maxn], x[maxn]; 13 int h[maxn]; 14 int height[maxn]; 15 void add_value(int u, int v, int i) 16 { 17 d[i].next = c[u]; 18 c[u] = i; 19 d[i].now = v; 20 } 21 void radix_sort(int l, int r) 22 { 23 for (int k = 1; k >= 0; k--) 24 { 25 memset(c, 0, sizeof(c)); 26 for (int i = r; i >= l; i--) 27 add_value(val[pos[i]][k], pos[i], i); 28 int t = 0; 29 for (int i = 0; i <= maxn; i++) 30 for (int j = c[i]; j; j = d[j].next)pos[++t] = d[j].now; 31 } 32 int t = 0; 33 for (int i = 1; i <= n; i++) 34 { 35 if (val[pos[i]][0] != val[pos[i - 1]][0] || val[pos[i]][1] != val[pos[i - 1]][1])t++; 36 Rank[pos[i]] = t; 37 } 38 } 39 void get_suffix_array() 40 { 41 int t = 1; 42 while (t / 2 <= n) 43 { 44 for (int i = 1; i <= n; i++) 45 { 46 val[i][0] = Rank[i]; 47 val[i][1] = (((i + t / 2 <= n) ? Rank[i + t / 2] : 0)); 48 pos[i] = i; 49 } 50 radix_sort(1, n); 51 t *= 2; 52 } 53 for (int i = 1; i <= n; i++) 54 SA[Rank[i]] = i; 55 } 56 void get_common_prefix() 57 { 58 memset(h, 0, sizeof(h)); 59 for (int i = 1; i <= n; i++) 60 { 61 if (Rank[i] == 1)h[i] = 0; 62 else 63 { 64 int now = 0; 65 if (i > 1 && h[i - 1] > 1)now = h[i - 1] - 1; 66 while (now + i <= n&&now + SA[Rank[i] - 1] <= n&&x[now + i] == x[now + SA[Rank[i] - 1]]) 67 now++; 68 h[i] = now; 69 } 70 } 71 for (int i = 1; i <= n; i++) 72 height[Rank[i]] = h[i]; 73 }
//之前说按照height的值进行分组,当存在一段连续的height满足条件时,说明这些height在一组里。当有一个height不满足时,这一组结束。则在这一组里找SA相差最大的一对,看是否满足条件 74 bool exist(int len) 75 { 76 77 int Min = n + 1, Max = 0; 78 for (int i = 1; i <= n; i++) 79 if (height[i] < len) 80 { 81 //如果已知的最大与最小值的差值满足条件,直接退出。 82 if (Max - Min >= len)return 1; 83 Min = Max = SA[i]; 84 } 85 else//在条件满足的情况下,使差值最大化 86 { 87 Min = min(Min, SA[i]); 88 Max = max(Max, SA[i]); 89 } 90 if (Max - Min >= len)return 1; 91 return 0; 92 } 93 int binary_search(int l, int r) 94 { 95 while (l <= r) 96 { 97 int mid = (l + r) / 2; 98 if (exist(mid))l = mid + 1; 99 else r = mid - 1; 100 } 101 return r; 102 } 103 void solve() 104 { 105 //题目限定 106 for (int i = 1; i <= n - 1; i++) 107 Rank[i] =x[i]= x[i + 1] - x[i] + 88; 108 n--; 109 get_suffix_array(); 110 get_common_prefix(); 111 int ans = binary_search(0, n) + 1; 112 ans = ((ans<5) ? 0 : ans); 113 printf("%d\n", ans); 114 } 115 int main() 116 { 117 while (scanf("%d\n", &n), n > 0) 118 { 119 for (int i = 1; i <= n; i++) 120 scanf("%d", &x[i]); 121 solve(); 122 } 123 return 0; 124 }
原文:https://www.cnblogs.com/nuchenghao/p/11296611.html