POJ 1743 不可重叠最长重复子串

#include <cstdio> using namespace std; //--------------------------------------------- //功能:2倍增算法生成后缀数组,参考罗穗骞论文 //sa:返回的后缀数组 //str:输入的串(这里为int数组) //n:串长度 //M:串中元素的最大值,最小值为0 //--------------------------------------------- #define MAX_N 19999 void suffix_array(int* sa, int* str, int n, int M) { static int pits[MAX_N]; //挖坑计数数组 static int aux[MAX_N]; //用于计算sa时的辅助数组 static int xx[MAX_N], yy[MAX_N]; int* x = xx, *y = yy, *t;//x[]总保存当前的rank int i, j, r, p, higher1, higher2; //先对所有后缀的第一个字符进行排序(采用挖坑式的基数排序,即统计每个字符的个数,以便在扫描时总能将字符放入合适的位置),放入sa中 for (i = 0; i <= M; ++i) { pits[i] = 0; } for (i = 0; i < n; ++i) { ++pits[x[i] = str[i]]; } for (i = 1; i <= M; ++i) //挖坑 { pits[i] += pits[i - 1]; } for (i = n - 1; i >= 0; --i) { sa[--pits[str[i]]] = i; } //进行若干趟基数排序 for (i = 1, r = M; i < n; i *= 2) { //先计算出第一关键字的各个坑,以便进行基数排序 for (j = 0; j <= r; ++j) { pits[j] = 0; } for (j = 0; j < n; ++j) { ++pits[x[j]]; } for (j = 1; j <= r; ++j) { pits[j] += pits[j - 1]; } //按第二关键字排序,排序之后放入aux数组中 for (j = n - i, p = 0; j < n; ++j, ++p) { aux[p] = j; } for (j = 0; j < n; ++j) { if (sa[j] >= i) { aux[p] = sa[j] - i; ++p; } } //从后到前扫描aux数组,根据挖好的第一关键字的坑收集到sa数组中,完成一次基数排序 for (j = n - 1; j >= 0; --j) { sa[--pits[x[aux[j]]]] = aux[j]; } //算出新的rank数组(即x[]) for (t = x, x = y, y = t, x[sa[0]] = 0, r = 0, j = 1; j < n; ++j) { higher1 = sa[j] + i; higher2 = sa[j - 1] + i; if (y[sa[j]] != y[sa[j - 1]] || (!(higher1 >= n && higher2 >= n || higher1 < n && higher2 < n && y[higher1] == y[higher2]))) { ++r; } x[sa[j]] = r; } } } //功能:根据后缀数组生成rank数组 void gen_rank(int* rank, int* sa, int n) { int i; for (i = 0; i < n; ++i) { rank[sa[i]] = i; } } //功能:根据串、后缀数组和排名数组计算height数组 void calc_height(int* height, int* str, int* sa, int* rank, int n) { //根据h[i]>=h[i-1]简化计算 int i, j, k; height[0] = 0; for (k = 0, i = 0; i < n; height[rank[i]] = k, ++i) { if (k) { --k; } if (rank[i] >= 1) { for (j = sa[rank[i] - 1]; j + k < n && i + k < n && str[i + k] == str[j + k]; ++k); } } } //功能:查找是否存在长度为k的不重叠重复子串 bool judge(int* sa, int* rank, int* height, int n, int k) { int mini, maxi, i; for (mini = maxi = sa[0], i = 1; i < n; ++i) { if (height[i] < k) { mini = maxi = sa[i]; } else if (sa[i] < mini) { mini = sa[i]; if (maxi - mini >= k) { return true; } } else if (sa[i] > maxi) { maxi = sa[i]; if (maxi - mini >= k) { return true; } } } return false; } int notes[20000], sa[19999], rank[19999], height[19999]; int main() { int N, i, low, high, mid; for (; ;) { //输入 scanf("%d", &N); if (!N) { break; } for (i = 0; i < N; ++i) { scanf("%d", &notes[i]); } for (i = N - 1; i >= 0; --i) { notes[i] -= notes[i - 1]; notes[i] += 87; } //生成后缀数组 suffix_array(sa, notes + 1, N - 1, 174); //生成rank数组 gen_rank(rank, sa, N - 1); //算出height数组 calc_height(height, notes + 1, sa, rank, N - 1); //二分寻找k low = 0; high = (N - 1) / 2; while (low < high) { mid = (low + high + 1) / 2; if (judge(sa, rank, height, N - 1, mid)) { low = mid; } else { high = mid - 1; } } if (low < 4) { puts("0"); } else { printf("%d/n", low + 1); } } return 0; }

 

几个输入数据:

10
1 2 3 4 5 6 7 8 9 10
8
1 1 2 1 1 1 1 2
30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18 82 78 74 70 66 67
64 60 65 80
0

 

我用这个模板提交了2774,不过速度很慢,G++下735ms:

#include <cstring> #include <cstdio> using namespace std; #define MAX_N 200002 void suffix_array(int* sa, char* str, int n, int M) { static int pits[MAX_N]; //挖坑计数数组 static int aux[MAX_N]; //用于计算sa时的辅助数组 static int xx[MAX_N], yy[MAX_N]; int* x = xx, *y = yy, *t;//x[]总保存当前的rank int i, j, r, p, higher1, higher2; //先对所有后缀的第一个字符进行排序(采用挖坑式的基数排序,即统计每个字符的个数,以便在扫描时总能将字符放入合适的位置),放入sa中 for (i = 0; i <= M; ++i) { pits[i] = 0; } for (i = 0; i < n; ++i) { ++pits[x[i] = str[i]]; } for (i = 1; i <= M; ++i) //挖坑 { pits[i] += pits[i - 1]; } for (i = n - 1; i >= 0; --i) { sa[--pits[str[i]]] = i; } //进行若干趟基数排序 for (i = 1, r = M; i < n; i *= 2) { //先计算出第一关键字的各个坑,以便进行基数排序 for (j = 0; j <= r; ++j) { pits[j] = 0; } for (j = 0; j < n; ++j) { ++pits[x[j]]; } for (j = 1; j <= r; ++j) { pits[j] += pits[j - 1]; } //按第二关键字排序,排序之后放入aux数组中 for (j = n - i, p = 0; j < n; ++j, ++p) { aux[p] = j; } for (j = 0; j < n; ++j) { if (sa[j] >= i) { aux[p] = sa[j] - i; ++p; } } //从后到前扫描aux数组,根据挖好的第一关键字的坑收集到sa数组中,完成一次基数排序 for (j = n - 1; j >= 0; --j) { sa[--pits[x[aux[j]]]] = aux[j]; } //算出新的rank数组(即x[]) for (t = x, x = y, y = t, x[sa[0]] = 0, r = 0, j = 1; j < n; ++j) { higher1 = sa[j] + i; higher2 = sa[j - 1] + i; if (y[sa[j]] != y[sa[j - 1]] || (!(higher1 >= n && higher2 >= n || higher1 < n && higher2 < n && y[higher1] == y[higher2]))) { ++r; } x[sa[j]] = r; } } } //功能:根据后缀数组生成rank数组 void gen_rank(int* rank, int* sa, int n) { int i; for (i = 0; i < n; ++i) { rank[sa[i]] = i; } } //功能:根据串、后缀数组和排名数组计算height数组 void calc_height(int* height, char* str, int* sa, int* rank, int n) { //根据h[i]>=h[i-1]简化计算 int i, j, k; height[0] = 0; for (k = 0, i = 0; i < n; height[rank[i]] = k, ++i) { if (k) { --k; } if (rank[i] >= 1) { for (j = sa[rank[i] - 1]; j + k < n && i + k < n && str[i + k] == str[j + k]; ++k); } } } char str[200001]; int sa[200001], rank[200001], height[200001]; int main() { int l1, l2, l, i, maxi; scanf("%s", str); l1 = strlen(str); str[l1] = 'z' + 1; scanf("%s", str + l1 + 1); l2 = strlen(str + l1 + 1); l = l1 + l2 + 1; for (i = 0; i < l; ++i) { str[i] -= 'a'; } suffix_array(sa, str, l, 26); gen_rank(rank, sa, l); calc_height(height, str, sa, rank, l); for (maxi = 0, i = 1; i < l; ++i) { if (height[i] > maxi && (sa[i - 1] < l1 && sa[i] > l1 || sa[i - 1] > l1 && sa[i] < l1)) { maxi = height[i]; } } printf("%d/n", maxi); return 0; }

你可能感兴趣的:(POJ 1743 不可重叠最长重复子串)