标签:def one print display ash 表示 logs 元素 元组
后缀数组基本模板
①倍增法(时间O(NlogN),空间O(N))
1 #include<iostream> 2 using namespace std; 3 const int maxl = 100010; 4 char s[maxl]; 5 int totlen; 6 int r2[maxl], cc[maxl],SA[maxl], RANK[maxl], Height[maxl]; 7 //r2:以第二关键字对后缀排序所得的辅助数组 8 //cc:计数排序辅助数组 9 //RANK:RANK数组,RANK[i]表示后缀i~totlen-1(Suffix[i])在所有后缀中的排名 10 //Height[i]:表示Suffix[SA[i]]和Suffix[SA[i - 1]]的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀 11 //SA[i]:后缀数组,满足Suffix[SA[1]] < Suffix[SA[2]] …… < Suffix[SA[Len]],即排名为i的后缀为Suffix[SA[i]] (与Rank是互逆运算) 12 bool Cmp(int *Rank, int idx1, int idx2, int len) 13 {//比较两个串是否相同,比较两个关键字 14 int a1 = Rank[idx1]; 15 int b1 = Rank[idx2]; 16 int a2 = (idx1 + len >= totlen ? -1 : Rank[idx1 + len]); 17 int b2 = (idx2 + len >= totlen ? -1 : Rank[idx2 + len]); 18 return a1 == b1&&a2 == b2; 19 } 20 void Build_SA() 21 { 22 int m = 26;// 单字符rank的范围 23 //计数排序 24 for (int i = 0; i < m; i++) cc[i] = 0; 25 for (int i = 0; i < totlen; i++) ++cc[RANK[i] = (s[i] - ‘a‘)]; 26 for (int i = 1; i < m; i++) cc[i] += cc[i - 1]; 27 for (int i = totlen - 1; i >= 0; --i) SA[--cc[RANK[i]]] = i; 28 29 for (int k = 1; k <= totlen; k <<= 1) 30 { 31 int p = 0; 32 33 for (int i = totlen - k; i < totlen; i++) 34 {//第二关键字为空的后缀放在最开头 35 r2[p++] = i; 36 } 37 for (int i = 0; i < totlen; i++) 38 {//接着放第二关键字不为空的 39 if (SA[i] >= k) r2[p++] = SA[i] - k; 40 } 41 //计数排序 42 for (int i = 0; i < m; i++) cc[i] = 0; 43 for (int i = 0; i < totlen; i++) ++cc[RANK[i]]; 44 //for (int i = 0; i < totlen; i++) ++cc[RANK[r2[i]]]; 45 for (int i = 1; i < m; i++) cc[i] += cc[i - 1]; 46 for (int i = totlen - 1; i >= 0; --i) SA[--cc[RANK[r2[i]]]] = r2[i]; 47 48 swap(RANK, r2); 49 m = 1; 50 RANK[SA[0]] = 0; 51 //r2指向旧的rank数组 52 for (int i = 1; i < totlen; i++) RANK[SA[i]] = (Cmp(r2, SA[i], SA[i - 1], k) ? m - 1 : m++); 53 54 if (m >= totlen) break; 55 } 56 } 57 void Build_Height() 58 { 59 for (int i = 0; i < totlen; i++) RANK[SA[i]] = i; 60 Height[0] = 0; 61 int k = 0; 62 for (int i = 0; i < totlen; i++) 63 { 64 if (!RANK[i]) continue; 65 int j = SA[RANK[i] - 1]; 66 if (k) k--; 67 while (s[i + k] == s[j + k]) k++; 68 Height[RANK[i]] = k; 69 } 70 } 71 72 int main() 73 { 74 scanf("%s", s); 75 totlen = strlen(s); 76 Build_SA(); 77 Build_Height(); 78 for (int i = 0; i < totlen; i++) printf("%d ", SA[i]); 79 printf("\n"); 80 for (int i = 0; i < totlen; i++) printf("%d ", Height[i]); 81 printf("\n"); 82 83 84 return 0; 85 } 86 //abcabcabcabc 87 //9 6 3 0 10 7 4 1 11 8 5 2 88 //0 3 6 9 0 2 5 8 0 1 4 7
———————————————————————————————————————————————————
———————————————————————————————————————————————————
1、poj 1743 Musical Theme
题意:给你一个长度为n(1<=n<=20000)的数字串。如果一个串在母串出现的次数大于一次那么这个串就是母串的重复子串。子串的每个位置同时加上一个数字重复出现在另一个位置也算重复。先在问这个母串最长的不相交即没有重复元素的重复子串的最大长度。
思路:后缀数组。对于题目所给的加上一个数字重复的情况。可以令s[i]=s[i+1]-s[i],直接转换成求字符串最长公共前缀。
求不相交重复子串:二分最大长度k。然后再用后缀数组判定。这样我们就可以将height分组,其中每组的后缀之间的height 值都不小于k。容易看出,有希望成为最长公共前缀不小于k 的两个后缀一定在同一组。然后对于每组后缀,只须判断每个后缀的sa 值的最大值和最小值之差是否不小于k。如果有一组满足,则说明存在,否则不存在。
1 //题意:给你一个长度为n(1<=n<=20000)的数字串。如果一个串在母串出现的次数大于一次那么这个串就是母串的重复子串。子串的每个位置同时加上一个数字重复出现在另一个位置也算重复。先在问这个母串最长的不相交即没有重复元素的重复子串的最大长度。 2 #include<iostream> 3 #define min(a,b) ((a)<(b)?(a):(b)) 4 #define max(a,b) ((a)>(b)?(a):(b)) 5 using namespace std; 6 const int MAX = 20050; 7 8 int n, num[MAX]; 9 int sa[MAX], Rank[MAX], height[MAX]; 10 int tp[MAX], wb[MAX], wv[MAX], tax[MAX]; 11 //sa:所有后缀的字典序中排第i位的在原串的起始位置为sa[i] 12 //Rank:原串中第i个位置开始的后缀在字典序的排名 13 //heiht:字典序中第i个和i-1个的后缀的最长公共前缀 14 //tp:rank的辅助数组(计数排序中的第二关键字),与SA意义一样。 15 //wb: 16 //wv: 17 //tax:基数排序辅助数组 18 int cmp(int *r, int a, int b, int l) 19 {//通过二元组两个下标的比较,确定两个子串是否相同 20 return r[a] == r[b] && r[a + l] == r[b + l]; 21 } 22 23 void getsa(int *r, int n, int m) 24 {//m为ASCII码的范围 25 int i, j, p, *x = tp, *y = wb, *t; 26 //基数排序 27 for (i = 0; i < m; i++) tax[i] = 0; 28 for (i = 0; i < n; i++) tax[x[i] = r[i]] ++; 29 for (i = 1; i < m; i++) tax[i] += tax[i - 1]; 30 for (i = n - 1; i >= 0; i--) sa[--tax[x[i]]] = i;//倒着枚举保证相对顺序 31 32 for (j = 1, p = 1; p < n; j *= 2, m = p) 33 {//把子串长度翻倍,更新rank 34 //j:当前一个字串的长度 35 //m:当前离散后的排名种类数 36 for (p = 0, i = n - j; i < n; i++) y[p++] = i; 37 for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;//按第二关键字排序.y[i]表示第二关键字排名第i的后缀起始位置 38 39 for (i = 0; i < n; i++) wv[i] = x[y[i]];//暂存 40 41 for (i = 0; i < m; i++) tax[i] = 0; 42 for (i = 0; i < n; i++) tax[wv[i]] ++;//x[i]表示起始位置为i的后缀的第一关键字排序 43 for (i = 1; i < m; i++) tax[i] += tax[i - 1]; 44 for (i = n - 1; i >= 0; i--) sa[--tax[wv[i]]] = y[i];//接着按第一关键字排序 45 46 for (t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i++) 47 {////x[i]存排名第i后缀的排名 48 x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++; 49 } 50 } 51 } 52 53 void calHeight(int *r, int n) 54 { 55 int i, j, k = 0; 56 for (i = 1; i <= n; i++) Rank[sa[i]] = i; 57 for (i = 0; i < n; height[Rank[i++]] = k) 58 { 59 for (k ? k-- : 0, j = sa[Rank[i] - 1]; r[i + k] == r[j + k]; k++); 60 } 61 } 62 63 bool valid(int len) 64 { 65 int i = 2, ma, mi;//区间下界和上界 66 while (1) 67 { 68 while (i <= n && height[i] < len) i++; 69 if (i > n) break; 70 ma = sa[i - 1]; 71 mi = sa[i - 1]; 72 while (i <= n && height[i] >= len) 73 { 74 ma = max(ma, sa[i]); 75 mi = min(mi, sa[i]); 76 i++; 77 } 78 if (ma - mi >= len) return true; 79 } 80 return false; 81 } 82 83 int main() 84 { 85 int i, ans; 86 while (scanf("%d", &n) && n != 0) 87 { 88 for (i = 0; i < n; i++) 89 { 90 scanf("%d", &num[i]); 91 } 92 if (n < 10) 93 {//如果小于10,则不相交重复子串字串长度不超过5,不符合题意 94 printf("0\n"); 95 continue; 96 } 97 n--; 98 for (i = 0; i < n; i++) 99 { 100 num[i] = num[i + 1] - num[i] + 89; 101 } 102 num[n] = 0; 103 getsa(num, n + 1, 200); 104 calHeight(num, n); 105 106 int low = 4, high = (n - 1) / 2, mid; 107 while (low < high) 108 { 109 mid = (low + high + 1) / 2; 110 if (valid(mid)) 111 { 112 low = mid; 113 } 114 else 115 { 116 high = mid - 1; 117 } 118 } 119 ans = low < 4 ? 0 : low + 1;//加回1 120 printf("%d\n", ans); 121 } 122 return 0; 123 }
2、UVA 12206 Stammering Aliens
题意:给定一个序列,求出现次数至少为m、长度最长的子串的最大起始下标
思路:对原串做完后缀数组后二分最大长度,对于每个二分值k,对height数组分组,如果某组中后缀数量大于等于m则找到这个组中sa[i]的最大值来更新答案值
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 int k; 6 7 //后缀数组模板 8 const int maxl = 40010; 9 char s[maxl]; 10 int totlen; 11 int r2[maxl], cc[maxl], SA[maxl], RANK[maxl], Height[maxl]; 12 //r2:以第二关键字对后缀排序所得的辅助数组 13 //cc:计数排序辅助数组 14 //RANK:RANK数组,RANK[i]表示后缀i~totlen-1(Suffix[i])在所有后缀中的排名 15 //Height[i]:表示Suffix[SA[i]]和Suffix[SA[i - 1]]的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀 16 //SA[i]:后缀数组,满足Suffix[SA[1]] < Suffix[SA[2]] …… < Suffix[SA[Len]],即排名为i的后缀为Suffix[SA[i]] (与Rank是互逆运算) 17 bool Cmp(int *Rank, int idx1, int idx2, int len) 18 {//比较两个串是否相同,比较两个关键字 19 int a1 = Rank[idx1]; 20 int b1 = Rank[idx2]; 21 int a2 = (idx1 + len >= totlen ? -1 : Rank[idx1 + len]); 22 int b2 = (idx2 + len >= totlen ? -1 : Rank[idx2 + len]); 23 return a1 == b1&&a2 == b2; 24 } 25 void Build_SA() 26 { 27 int m = 26;// 单字符rank的范围 28 //计数排序 29 for (int i = 0; i < m; i++) cc[i] = 0; 30 for (int i = 0; i < totlen; i++) ++cc[RANK[i] = (s[i] - ‘a‘)]; 31 for (int i = 1; i < m; i++) cc[i] += cc[i - 1]; 32 for (int i = totlen - 1; i >= 0; --i) SA[--cc[RANK[i]]] = i; 33 34 for (int k = 1; k <= totlen; k <<= 1) 35 { 36 int p = 0; 37 38 for (int i = totlen - k; i < totlen; i++) 39 {//第二关键字为空的后缀放在最开头 40 r2[p++] = i; 41 } 42 for (int i = 0; i < totlen; i++) 43 {//接着放第二关键字不为空的 44 if (SA[i] >= k) r2[p++] = SA[i] - k; 45 } 46 //计数排序 47 for (int i = 0; i < m; i++) cc[i] = 0; 48 for (int i = 0; i < totlen; i++) ++cc[RANK[i]]; 49 //for (int i = 0; i < totlen; i++) ++cc[RANK[r2[i]]]; 50 for (int i = 1; i < m; i++) cc[i] += cc[i - 1]; 51 for (int i = totlen - 1; i >= 0; --i) SA[--cc[RANK[r2[i]]]] = r2[i]; 52 53 swap(RANK, r2); 54 m = 1; 55 RANK[SA[0]] = 0; 56 //r2指向旧的rank数组 57 for (int i = 1; i < totlen; i++) RANK[SA[i]] = (Cmp(r2, SA[i], SA[i - 1], k) ? m - 1 : m++); 58 59 if (m >= totlen) break; 60 } 61 } 62 void Build_Height() 63 { 64 for (int i = 0; i < totlen; i++) RANK[SA[i]] = i; 65 Height[0] = 0; 66 int k = 0; 67 for (int i = 0; i < totlen; i++) 68 { 69 if (!RANK[i]) continue; 70 int j = SA[RANK[i] - 1]; 71 if (k) k--; 72 while (s[i + k] == s[j + k]) k++; 73 Height[RANK[i]] = k; 74 } 75 } 76 77 78 79 int Judge(int x) 80 { 81 int ans = -1; 82 for (int i = 0; i < totlen; i++) 83 { 84 if (totlen - SA[i] < x)continue; 85 int tans = SA[i], cnt = 1; 86 while (i + 1 < totlen&&Height[i + 1] >= x) 87 { 88 tans = max(tans, SA[i + 1]); 89 cnt++; 90 i++; 91 } 92 if (cnt >= k) ans = max(ans, tans); 93 } 94 return ans; 95 } 96 void Solve() 97 { 98 if (Judge(1) == -1) 99 { 100 printf("none\n"); 101 return; 102 } 103 int l = 1, r = r = totlen,ans; 104 while (l <= r) 105 { 106 int mid = (l + r) / 2; 107 int tmp = Judge(mid); 108 if ( tmp!= -1) l = mid + 1,ans=tmp; 109 else r = mid - 1; 110 } 111 printf("%d %d\n", r, ans); 112 } 113 int main() 114 { 115 while (~scanf("%d", &k) && k) 116 { 117 scanf("%s", s); 118 totlen = strlen(s); 119 Build_SA(); 120 Build_Height(); 121 //for (int i = 0; i < totlen; i++) printf("%d ", SA[i]); 122 //printf("\n"); 123 //for (int i = 0; i < totlen; i++) printf("%d ", Height[i]); 124 //printf("\n"); 125 Solve(); 126 } 127 return 0; 128 }
标签:def one print display ash 表示 logs 元素 元组
原文地址:http://www.cnblogs.com/ivan-count/p/7368154.html