【最长重复子串问题】
可重叠最长重复子串 -- POJ 3261
Disc: 给出包含n个元素的数组a,问其中最长可重叠重复子串的长度,要求该子串至少重复k次;输入保证至少存在一个重复k次的最长子串;
Tips: 二分查找子串的长度,注意对该长度的子串是否存在K个重复子串的判定方法;
代码:
1 /* 2 Problem: POJ 3261 3 Tips: 可重复最长重复子串,后缀数组,二分 4 Time: 172ms 5 Date: 2015.8.31 6 */ 7 #include <iostream> 8 #include <cstring> 9 #include <cstddef> 10 #include <cstdio> 11 #include <string> 12 #include <algorithm> 13 const int maxn = 100005; 14 const int maxm = 1000005; 15 int wa[maxm],wb[maxn],wv[maxn],ws[maxm]; 16 int sa[maxn], rank[maxn],height[maxn]; 17 int cmp(int *rank, int a,int b,int l) 18 { 19 return rank[a]==rank[b] && rank[a+l]==rank[b+l]; 20 } 21 void debug1(int* r, int n) 22 { 23 for(int i = 0; i < n; i++) 24 { 25 printf("sa[%d]: %d ", i, sa[i]); 26 int p = sa[i]; 27 for(; p < n; p++) 28 printf("%d ", r[p]); 29 printf(" "); 30 } 31 } 32 void debug2(int* r, int n) 33 { 34 for(int i = 0; i < n; i++) 35 { 36 printf("height[rank[%d]]: %d ", i, height[rank[i]]); 37 } 38 } 39 void da(int *r,int n,int m) 40 { 41 int i, k, p, *x=wa, *y=wb, *t; 42 for(i=0;i<m;i++) ws[i] = 0; 43 for(i=0;i<n;i++) ws[x[i] = r[i]]++; 44 for(i=1;i<m;i++) ws[i] += ws[i-1]; 45 for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i; 46 for(k=1, p=1; p<n; k*=2, m=p) 47 { 48 for(p=0,i=n-k;i<n;i++) y[p++]=i; 49 for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; 50 for(i=0;i<n;i++) wv[i]=x[y[i]]; 51 for(i=0;i<m;i++) ws[i]=0; 52 for(i=0;i<n;i++) ws[wv[i]]++; 53 for(i=1;i<m;i++) ws[i]+=ws[i-1]; 54 for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i]; 55 t=x,x=y,y=t; 56 for(p=1,x[sa[0]]=0,i=1;i<n;i++) 57 x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++; 58 } 59 //debug1(r, n); 60 return; 61 } 62 void calheight(int *r,int n) 63 { 64 int i,j,k=0; 65 for(i=1;i<=n;i++) rank[sa[i]]=i; 66 for(i=0;i<n;height[rank[i++]]=k) 67 for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++) 68 ; 69 //debug2(r, n); 70 return; 71 } 72 bool check(int mid, int n, int k) //Notice! 73 { 74 int i = 1; //height[0] = 0; 75 while(1) 76 { 77 while(i <= n && height[i] < mid) i++; 78 if(i == n+1) break; 79 int cnt = 1; 80 while(i <= n && height[i] >= mid) i++, cnt++; 81 if(cnt >= k) return true; 82 } 83 return false; 84 } 85 int bin_search(int n, int k) 86 { 87 int l = 1, r = n, mid; 88 int ans = 0; 89 while(l <= r) 90 { 91 mid = l+(r-l)/2; 92 if(check(mid, n, k)) 93 ans = mid, l = mid+1; 94 else 95 r = mid-1; 96 } 97 return ans; 98 } 99 int main() 100 { 101 int n, k, r[maxn]; 102 scanf("%d%d", &n, &k); 103 for(int i = 0; i < n; i++) {scanf("%d", &r[i]); r[i]++;} 104 r[n] = 0; 105 da(r,n+1,maxm); 106 calheight(r,n); 107 int ans = bin_search(n, k); 108 printf("%d ", ans); 109 return 0; 110 }
不可重叠最长重复子串 -- POJ 1743
Disc: 给出一段长度为n的数组,其元素大小在1-88之间,现在要求出这个串中最长的不重复的两个相同的子串,满足要求的子串的定义是长度不短于5,其两个子串可以通过分别加上某一值或减去某一值得到,两子串不能够重叠。
Tips: 转化为相邻两元素差数组,求其不可重叠最长重复子串,且长度不可小于4;
二分查找子串的长度,将height[]数组分组,判断连续的height[]大于等于k的一段中sa[i] -sa[j] 是否 >= k (height[i , j+1] >= k),若存在此情况,则证明该长度下存在不重叠重复子串;
代码:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cstdio> 5 #include <string> 6 #include <algorithm> 7 const int maxn = 100001; 8 int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; 9 int rank[maxn],height[maxn],sa[maxn]; 10 int cmp(int *rank, int a,int b,int l) 11 { 12 return rank[a]==rank[b] && rank[a+l]==rank[b+l]; 13 } 14 void da(int *r,int n,int m) 15 { 16 int i, k, p, *x=wa, *y=wb, *t; 17 18 for(i=0;i<m;i++) ws[i] = 0; 19 for(i=0;i<n;i++) ws[x[i] = r[i]]++; 20 for(i=1;i<m;i++) ws[i] += ws[i-1]; 21 for(i=n-1;i>=0;i--) sa[--ws[x[i]]] = i; 22 for(k=1, p=1; p<n; k*=2, m=p) 23 { 24 p=0; for(i=n-k;i<n;i++) y[p++]=i; 25 for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; 26 for(i=0;i<n;i++) wv[i]=x[y[i]]; 27 for(i=0;i<m;i++) ws[i]=0; 28 for(i=0;i<n;i++) ws[wv[i]]++; 29 for(i=1;i<m;i++) ws[i]+=ws[i-1]; 30 for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i]; 31 t=x,x=y,y=t; 32 for(p=1,x[sa[0]]=0,i=1;i<n;i++) 33 x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++; 34 } 35 return; 36 } 37 38 void calheight(int *r,int n) 39 { 40 int i,j,k=0; 41 for(i=1;i<=n;i++) rank[sa[i]]=i; 42 for(i=0;i<n;height[rank[i++]]=k) 43 for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++) 44 ; 45 return; 46 } 47 bool check(int k, int n) 48 { 49 for(int i = 1; i <= n; i++) 50 { 51 if(height[i] < k) continue; 52 for(int j = i-1; j >= 1; j--) 53 { 54 if(abs(sa[i]-sa[j]) >= k) return true; 55 if(height[j] < k) break; 56 } 57 } 58 return false; 59 } 60 int bin_search(int n) 61 { 62 int l = 1, r = n, mid, ans = 0; 63 while(l <= r) 64 { 65 mid = l+(r-l)/2; 66 if(check(mid, n)) 67 ans = mid, l = mid+1; 68 else 69 r = mid-1; 70 } 71 return ans; 72 } 73 74 int main() 75 { 76 int n, r[maxn]; 77 while(~scanf("%d", &n) && n) 78 { 79 n--; 80 int a, b; scanf("%d", &a); 81 for(int i = 0; i < n; i++) 82 { 83 scanf("%d", &b); 84 r[i] = b-a+100; 85 a = b; 86 } 87 r[n] = 0; 88 89 if(n<10) {printf("0 "); continue;} 90 91 da(r,n+1,200); 92 calheight(r,n); 93 94 int ans = bin_search(n); 95 printf("%d ", ans>=4 ? ++ans : 0); 96 } 97 return 0; 98 }
【回文子串问题】
最长回文子串 -- URAL 1297
Disc: 给出字符串,输出其最长回文子串
Tips: 对于字符串上的每个位置i,如果知道从i开始的后缀和到i为止的前缀反转后的字符串的最长公共前缀的话,就知道了以第i个字符为对称中心的最长回文的长度了;
因此我们用在S中不会出现的字符('$')将S和S反转后的字符串拼接起来,得到字符串S',再计算S'的后缀数组和height[]数组。
对height数组进行相应区间查询最小值RMQ,奇数回文串即区间[rank[i], rank[n-i-1]]中height的最小值,偶数回文串即区间[rank[i], rank[n-i]]中height的最小值
代码:
1 /* 2 Problem: URAL 1297 3 Disc: 给出字符串,求最长回文子串 4 Tips: Suffix Array, height[], RMQ 5 对于字符串上的每个位置i,如果知道从i开始的后缀和到i为止的前缀反转后的字符串的最长公共前缀的话,就知道了以第i个字符为对称中心的最长回文的长度了; 6 因此我们用在S中不会出现的字符('$')将S和S反转后的字符串拼接起来,得到字符串S',再计算S'的后缀数组。 7 Date: 2015.9.1 8 */ 9 #include<iostream> 10 #include<cstdio> 11 #include<cstring> 12 using namespace std; 13 const int maxn = 1002; 14 int sa[maxn], rank[maxn], height[maxn]; 15 int wa[maxn], wb[maxn], wv[maxn], wd[maxn]; 16 int cmp(int *r, int a, int b, int l){ 17 return r[a] == r[b] && r[a+l] == r[b+l]; 18 } 19 void da(int *r, int n, int m){ // 倍增算法 r为待匹配数组 n为总长度 m为字符范围 20 int i, j, p, *x = wa, *y = wb, *t; 21 for(i = 0; i < m; i ++) wd[i] = 0; 22 for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++; 23 for(i = 1; i < m; i ++) wd[i] += wd[i-1]; 24 for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i; 25 for(j = 1, p = 1; p < n; j *= 2, m = p){ 26 for(p = 0, i = n-j; i < n; i ++) y[p ++] = i; 27 for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j; 28 for(i = 0; i < n; i ++) wv[i] = x[y[i]]; 29 for(i = 0; i < m; i ++) wd[i] = 0; 30 for(i = 0; i < n; i ++) wd[wv[i]] ++; 31 for(i = 1; i < m; i ++) wd[i] += wd[i-1]; 32 for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i]; 33 for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++){ 34 x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++; 35 } 36 } 37 } 38 void calheight(int *r, int n){ // 求height数组。 39 int i, j, k = 0; 40 for(i = 1; i <= n; i ++) rank[sa[i]] = i; 41 for(i = 0; i < n; height[rank[i ++]] = k){ 42 for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++); 43 } 44 } 45 46 /*******************非模板区***********************/ 47 int Log2[maxn]; 48 void init() 49 { 50 Log2[0] = -1; Log2[1] = 0; 51 int i, j; 52 for(i = 2, j = 0; i < maxn; i++) 53 { 54 if(i > (1<<(j+1))) j++; 55 Log2[i] = j; 56 } 57 } 58 int dp[maxn][20]; 59 void initRMQ(int n) 60 { 61 for(int i = 1; i <= n; i++) dp[i][0] = height[i]; 62 for(int j = 1; j <= 20; j++) 63 for(int i = 1; i + (1 << (j-1)) <= n; i++) 64 dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]); //注意是height[]的最小值 65 } 66 int query(int L, int R) 67 { 68 if(L > R) swap(L, R); 69 L++; 70 if(L == R) return dp[L][0]; 71 int len = R-L+1; 72 int k = Log2[len]; //此处用的是Log2[]数组保存每个数的log值,当然也可以用下面的式子求得; 73 //int k = (int)(log(des - src + 1.0) / log(2.0)); 74 return min(dp[L][k], dp[R-(1<<k)+1][k]); 75 } 76 void solve(char* s, int n) 77 { 78 initRMQ(n); 79 int slen = strlen(s); 80 int maxl = 1, l, len, start; 81 for(int i = 0; i < slen; i++) 82 { 83 l = query(rank[i], rank[n-i-1]); //奇数回文串 84 len = l*2-1; 85 if(len > maxl) maxl = len, start = i-l+1; 86 87 l = query(rank[i], rank[n-i]); //偶数回文串 88 len = l*2; 89 if(len > maxl) maxl = len, start = i-l; 90 } 91 if(maxl == 1) printf("%c ", s[0]); 92 else 93 { 94 for(int i = start; i < start + maxl; i++) 95 printf("%c", s[i]); 96 printf(" "); 97 } 98 } 99 int main() 100 { 101 init(); 102 char s[maxn]; 103 int r[maxn*2]; 104 while(~scanf("%s", s)) 105 { 106 int len = strlen(s), n = 0; 107 for(int i = 0; i < len; i++) r[n++] = s[i]; 108 r[n++] = '$'; 109 for(int i = len-1; i >= 0; i--) r[n++] = s[i]; 110 r[n] = 0; 111 da(r,n+1,128); 112 calheight(r,n); 113 solve(s,n); 114 } 115 return 0; 116 }
【公共子串问题】
最长公共连续子串 --POJ 2774
Disc: 给定两个字符串A和B,求最长公共连续子串
Tips: 首先将两个字符串合并成一个字符串,中间由一个字符串中不可能出现的字符分割。通过二分枚举长度,然后在height分组中查看是否存在在两个不同串的后缀即可。
也可以证明最长的公共子串所在后缀是相邻的,因此可以直接遍历一遍height数组,判定相邻的两个height数组是否属于不同的两个串。
code:
1 /* 2 Problem: POJ 2774 3 Tips: 最长公共连续子串 4 Date: 2015.8.30 5 */ 6 #include<iostream> 7 #include<cstdio> 8 #include<cstring> 9 using namespace std; 10 const int maxn = 200002; 11 int sa[maxn], rank[maxn], height[maxn]; 12 int wa[maxn], wb[maxn], wv[maxn], wd[maxn]; 13 int cmp(int *r, int a, int b, int l){ 14 return r[a] == r[b] && r[a+l] == r[b+l]; 15 } 16 void da(int *r, int n, int m){ // 倍增算法 r为待匹配数组 n为总长度 m为字符范围 17 int i, j, p, *x = wa, *y = wb, *t; 18 for(i = 0; i < m; i ++) wd[i] = 0; 19 for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++; 20 for(i = 1; i < m; i ++) wd[i] += wd[i-1]; 21 for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i; 22 for(j = 1, p = 1; p < n; j *= 2, m = p){ 23 for(p = 0, i = n-j; i < n; i ++) y[p ++] = i; 24 for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j; 25 for(i = 0; i < n; i ++) wv[i] = x[y[i]]; 26 for(i = 0; i < m; i ++) wd[i] = 0; 27 for(i = 0; i < n; i ++) wd[wv[i]] ++; 28 for(i = 1; i < m; i ++) wd[i] += wd[i-1]; 29 for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i]; 30 for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++){ 31 x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++; 32 } 33 } 34 } 35 void calheight(int *r, int n){ // 求height数组。 36 int i, j, k = 0; 37 for(i = 1; i <= n; i ++) rank[sa[i]] = i; 38 for(i = 0; i < n; height[rank[i ++]] = k){ 39 for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++); 40 } 41 } 42 void solve(int n, int l1, int l2) 43 { 44 int maxl = 0; 45 for(int i = 2; i < n; i++) 46 { 47 if(height[i] > maxl) 48 { 49 if(sa[i-1] >= 0 && sa[i-1] < l1 && sa[i] > l1) 50 maxl = height[i]; 51 if(sa[i] >= 0 && sa[i] < l1 && sa[i-1] > l1) 52 maxl = height[i]; 53 } 54 } 55 printf("%d ", maxl); 56 } 57 int main() 58 { 59 char s1[maxn], s2[maxn]; 60 int s[maxn], r[maxn]; 61 scanf("%s%s", s1, s2); 62 int l1 = strlen(s1), l2 = strlen(s2); 63 int n = 0; 64 for(int i = 0; i < l1; i++) 65 s[n++] = (s1[i]-'a'+1); 66 s[n++] = 28; 67 for(int i = 0; i < l2; i++) 68 s[n++] = (s2[i]-'a'+1); 69 s[n] = 0; 70 da(s,n+1,30); 71 calheight(s,n); 72 solve(n, l1, l2); 73 return 0; 74 }