Poj 2774 Long Long Message (后缀数组)
题目链接:
题目描述:
给出A,B两个字符串,求最长公共子串?
解题思路:
求A,B字符串的最长公共子串可以转化为求A,B字符串后缀数组的最长公共前缀。把B串连接在A串后面,用'$'隔开组成r串。求出r串的height数组,最大的height[i](满足sa[i]与sa[i-1]不在同一文本串中)就是答案。时间复杂度大概为O((|A|+|B|)*log(|A|+|B|))。(阅兵假期用来搞这个,感觉还是有点怪怪的感觉,不过秉承着以前的学习风格,先记下模板,以后在水题中慢慢体会好了)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 typedef long long LL; 7 const int maxn = 200010; 8 9 int sa[maxn], rank[maxn], height[maxn]; 10 int t1[maxn], t2[maxn], r[maxn], c[maxn]; 11 bool cmp (int *str, int a, int b, int k) 12 {//rank相邻的两个串,第一第二关键字都一样,rank一样 13 return str[a]==str[b] && str[a+k]==str[b+k]; 14 } 15 int da (int *str, int n, int m) 16 { 17 int *x = t1, *y = t2, i, j; 18 n ++; 19 //基数排序, 20 for (i=0; i<m; i++) c[i] = 0; 21 for (i=0; i<n; i++) c[x[i] = str[i]] ++; 22 for (i=1; i<m; i++) c[i] += c[i-1]; 23 for (i=n-1; i>=0; i--) sa[-- c[str[i]]] = i; 24 for (j=1; j<=n; j*=2) 25 { 26 //用sa数组对第二关键字排序 27 int p = 0; 28 for (i=n-j; i<n; i++) y[p++] = i; //不能添加长度为j的串,第二关键字附为最小 29 for (i=0; i<n; i++) if (sa[i] >= j) y[p++] = sa[i] - j; 30 //枚举rank,起点大于j的串,才能倍增 31 //更新sa数组 32 for (i=0; i<m; i++) c[i] = 0; 33 for (i=0; i<n; i++) c[x[y[i]]] ++; 34 for (i=1; i<m; i++) c[i] += c[i-1]; 35 for (i=n-1; i>=0; i--) sa[-- c[x[y[i]]]] = y[i]; 36 //更新x数组 37 swap (x, y); 38 p = 1, x[sa[0]] = 0; 39 for (i=1; i<n; i++) 40 x[sa[i]] = cmp(y, sa[i-1], sa[i], j)?p-1:p++; 41 if (p >= n) 42 break; 43 m = p; 44 } 45 //计算rank数组 46 for (i=0; i<n; i++) 47 rank[sa[i]] = i; 48 //计算height数组 49 n --; 50 int k = 0; 51 for (i=0; i<n; i++) 52 {//枚举起点, height[名次] 53 if (k) k --; 54 j = sa[rank[i]-1]; 55 while (str[i+k]==str[j+k]) k++; 56 height[rank[i]] = k; 57 } 58 } 59 int main () 60 { 61 char str1[maxn/2], str2[maxn/2]; 62 while (scanf ("%s %s", str1, str2) != EOF) 63 { 64 int n1 = strlen(str1); 65 int n2 = strlen(str2); 66 int n = n1 + n2 + 1; 67 for (int i=0; i<n; i++) 68 { 69 if (i < n1) 70 r[i] = str1[i]; 71 else if (i == n1) 72 r[i] = '$'; 73 else 74 r[i] = str2[i-n1-1]; 75 } 76 r[n] = 0; 77 da (r, n, 128); 78 int ans = 0; 79 for (int i=1; i<n; i++) 80 {//i为rank 81 if (sa[i]<n1&&sa[i-1]>n1 || sa[i]>n1&&sa[i-1]<n1) 82 //rank相邻的后缀串不在同一个字符串 83 ans = max(ans, height[i]); 84 } 85 printf ("%d\n", ans); 86 } 87 return 0; 88 }
本文为博主原创文章,未经博主允许不得转载。