转自:http://www.cnblogs.com/ziyi--caolu/p/3195342.html
http://poj.org/problem?id=1743
题意:给出一串字符,求不重合的最长重复子串..........
我自己的一点想法:编完后发现,其实就是将height值分组,然后记录在二分答案时满足height值>=p的sa[i]的最大最小值,然后要是最大值减去最小值会>=p,这就说明两个子串的lcp值>=p并且它们的坐标也相差>=p,就自然满足题意.........
#include<iostream> #include<algorithm> #include<string> #include<map>//int dx[4]={0,0,-1,1};int dy[4]={-1,1,0,0}; #include<set>//int gcd(int a,int b){return b?gcd(b,a%b):a;} #include<vector> #include<cmath> #include<queue> #include<string.h> #include<stdlib.h> #include<cstdio> #define mod 1e9+7 #define ll long long using namespace std; #define maxn 20050 int wa[maxn], wb[maxn], wv[maxn], wc[maxn]; int r[maxn], sa[maxn], rank[maxn], height[maxn]; int n; int cmp(int *r, int a, int b, int l) { return r[a] == r[b] && r[a+l] == r[b+l]; } void da() { //m为最大字符 int i, j, p, *x = wa, *y = wb, *t, m = 256; for(i = 0; i < m; i++) wc[i] = 0; for(i = 0; i <= n; i++) wc[x[i] = r[i]]++; for(i = 1; i < m; i++) wc[i] += wc[i-1]; for(i = n; i >= 0; i--) sa[--wc[x[i]]] = i; for(j = 1, p = 1; p < n; j *= 2, m = p) { for(p = 0, i = n - j + 1; i <= n; i++) y[p++] = i; for(i = 0; i <= n; i++) if(sa[i] >= j) y[p++] = sa[i] - j; for(i = 0; i <= n; i++) wv[i] = x[y[i]]; for(i = 0; i < m; i++) wc[i] = 0; for(i = 0; i <= n; i++) wc[wv[i]]++; for(i = 1; i < m; i++) wc[i] += wc[i-1]; for(i = n; i >= 0; i--) sa[--wc[wv[i]]] = y[i]; for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i <= n; i++) x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1 : p++; } } void calheight() { int i, j, k = 0; for(i = 1; i <= n; i++) rank[sa[i]] = i; for(i = 0; i < n; height[rank[i++]] = k) for(k ? k-- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k++); } bool check(int mid) { int minn = sa[1], maxx = sa[1]; for(int i = 2; i <= n; i++) { if(height[i] >= mid) { minn = min(minn, sa[i]); maxx = max(maxx, sa[i]); if(maxx - minn >= mid) return 1; } else minn = maxx = sa[i]; } return false; } void solve() { da();//求sa数组 calheight();//求rank数组和height数组 int l = 1, r = n, ans = -1; while(l <= r) { int mid = (l + r) >> 1; if(check(mid)) ans = mid, l = mid + 1; else r = mid - 1; } if(ans >= 4) printf("%d\n", ans + 1); else printf("0\n"); } int main() { int a, b; while(~scanf("%d", &n)) { if(!n) break; n--; scanf("%d", &b); for(int i = 0; i < n; i++) { scanf("%d", &a); r[i] = a - b + 100; b = a; } r[n] = 0; solve(); } return 0; }
题意:可重叠的k次最长重复子串。
#include<iostream> using namespace std; const int MAX = 20050; const int M = 20000; // M的值题意应为1000000,则对应的wd[]的长度也应为1000000。 int n, k, num[MAX]; int sa[MAX], rank[MAX], height[MAX]; int wa[MAX], wb[MAX], wv[MAX], wd[MAX]; int cmp(int *r, int a, int b, int l) { return r[a] == r[b] && r[a+l] == r[b+l]; } void da(int *r, int n, int m) { int i, j, p, *x = wa, *y = wb, *t; for(i = 0; i < m; i ++) wd[i] = 0; for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++; for(i = 1; i < m; i ++) wd[i] += wd[i-1]; for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i; for(j = 1, p = 1; p < n; j *= 2, m = p) { for(p = 0, i = n-j; i < n; i ++) y[p ++] = i; for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j; for(i = 0; i < n; i ++) wv[i] = x[y[i]]; for(i = 0; i < m; i ++) wd[i] = 0; for(i = 0; i < n; i ++) wd[wv[i]] ++; for(i = 1; i < m; i ++) wd[i] += wd[i-1]; for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i]; for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++) { x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1 : p ++; } } } void calHeight(int *r, int n) { int i, j, k = 0; for(i = 1; i <= n; i ++) rank[sa[i]] = i; for(i = 0; i < n; height[rank[i ++]] = k) { for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++); } } bool valid(int len) { int i = 2, cnt; while(1) { while(i <= n && height[i] < len) i ++; if(i > n) break; cnt = 1; while(i <= n && height[i] >= len) { cnt ++; i ++; } if(cnt >= k) return true; } return false; } int main() { cin >> n >> k; for(int i = 0; i < n; i ++) { cin >> num[i]; } num[n] = 0; da(num, n + 1, M); calHeight(num, n); int low = 1, high = n, mid; while(low < high) { mid = (low+high+1) / 2; if(valid(mid)) { low = mid; }else { high = mid - 1; } } cout << low << endl; return 0; }