HDU-1403-Longest Common Substring(后缀数组的高度数组运用)
这题要求两个串中的最长相同子串的长度。高度数组可以求一个串中的最长相同子串的长度。所以想到把两个串连起来,但是这样又会产生一些新的串(第一个串的结尾和第二个串的开头组成的)于是在两个串中间放一个'\0'分隔,正好'\0'是字符里最小的,不会对第一个串的排序产生影响。
Accepted | 1403 | 62MS | 5344K | 2117 B | G++ |
#include "bits/stdc++.h" using namespace std; const int MAXN = 2e5 + 5; char s[MAXN]; int x[MAXN], y[MAXN], cnt[MAXN]; int sa[MAXN], rk[MAXN], height[MAXN]; int a, b, ans; void getSa(int n, int m) { for (int i = 0; i <= m; i++) { cnt[i] = 0; } for (int i = 1; i <= n; i++) { cnt[x[i] = s[i]]++; } for (int i = 1; i <= m; i++) { cnt[i] += cnt[i - 1]; } for (int i = n; i; i--) { sa[cnt[x[i]]--] = i; } int num = 1; for (int j = 1; num < n; j <<= 1, m = num) { num = 0; for (int i = n - j + 1; i <= n; i++) { y[++num] = i; } for (int i = 1; i <= n; i++) { if (sa[i] > j) { y[++num] = sa[i] - j; } } for (int i = 0; i <= m; i++) { cnt[i] = 0; } for (int i = 1; i <= n; i++) { cnt[x[i]]++; } for (int i = 1; i <= m; i++) { cnt[i] += cnt[i - 1]; } for (int i = n; i; i--) { sa[cnt[x[y[i]]]--] = y[i]; } swap(x, y); num = 1; x[sa[1]] = 1; for (int i = 1; i <= n; i++) { if (y[sa[i]] != y[sa[i - 1]] || y[sa[i] + j] != y[sa[i - 1] + j]) { x[sa[i]] = ++num; } else { x[sa[i]] = num; } } } } void getHeight(int n) { for (int i = 1; i <= n; i++) { rk[sa[i]] = i; } int k = 0; for (int i = 1; i <= n; i++) { int j = sa[rk[i] - 1]; k = k ? k - 1 : k; while (s[i + k] == s[j + k]) { k++; } // height[rk[i]] = k; // 如果i和j位于前后两个不同的串,更新ans if ((i <= a + 1) ^ (j <= a + 1)) { ans = max(ans, k); } } } int main() { while (~scanf("%s", s + 1)) { a = strlen(s + 1); // 把第二个字符串直接输入到第一个字符串的'\0'后面,例如输入两个"abc"就是"\0abc\0abc\0" scanf("%s", s + a + 2); b = strlen(s + a + 2); // n表示第一个串的长度,m表示第二个串的长度,加上中间的'\0'要求后缀数组的部分长度为n + m + 1,也就是"abc\0abc"这部分 getSa(a + b + 1, 130); ans = 0; // 在求高度数组的时候为避免第二个串后面的'\0'和第一个串后面的'\0'对结果造成影响。"abc\0" == "abc\0" 将第二个串后面的'\0'改成字符串中不可能出现的字符 s[a + b + 2] = '0'; getHeight(a + b + 1); printf("%d\n", ans); } return 0; }