【hdu 1403】Longest Common Substring
【链接】h在这里写链接
【题意】
求两个串的最长公共子串。
【题解】
Sa[i]表示的是字典序为i的后缀的起始位置。
可以把两个字符串合在一起(中间用一个比'z'大的字符分割);
则如果Sa[i-1]<len1且Sa[i]>len1;
则它们表示s1和s2的两个后缀。
取height[i]然后求最大值就可以了。
(height[i]表示的是字典序为i-1和字典序为i的后缀它们的最长公共前缀)
在这里,也即两个子串了。
【错的次数】
5+
【反思】
桶的大小得设置成2e5才行。。不是字符ascill码的最大值。。
【代码】
#include<bits/stdc++.h> using namespace std; const int N = 2e5; const int MAX_CHAR = 255;//每个数字的最大值。 char s[N + 10];//如果是数字,就写成int s[N+10]就好,从0开始存 int Sa[N + 10], T1[N + 10], T2[N + 10], C[N+10]; //Rank数组下标是从0开始的,表示的是从i开始的后缀的排名 //Sa数组又是从1开始的,表示的是第i名的(i>=1)后缀是从字符串中的第几个字符开始的. int Height[N + 10], Rank[N + 10]; void build_Sa(int n, int m) { int i, *x = T1, *y = T2; for (i = 0; i<m; i++) C[i] = 0; for (i = 0; i<n; i++) C[x[i] = s[i]]++; for (i = 1; i<m; i++) C[i] += C[i - 1]; for (i = n - 1; i >= 0; i--) Sa[--C[x[i]]] = i; for (int k = 1; k <= n; k <<= 1) { int p = 0; for (i = n - k; i<n; i++) y[p++] = i; for (i = 0; i<n; i++) if (Sa[i] >= k) y[p++] = Sa[i] - k; for (i = 0; i<m; i++) C[i] = 0; for (i = 0; i<n; i++) C[x[y[i]]]++; for (i = 1; i<m; i++) C[i] += C[i - 1]; for (i = n - 1; i >= 0; i--) Sa[--C[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[Sa[0]] = 0; for (i = 1; i<n; i++) x[Sa[i]] = y[Sa[i - 1]] == y[Sa[i]] && y[Sa[i - 1] + k] == y[Sa[i] + k] ? p - 1 : p++; if (p >= n) break; m = p; } } void getHeight(int n) { int i, j, k = 0; for (i = 1; i <= n; i++) Rank[Sa[i]] = i; for (i = 0; i<n; i++) { if (k) k--; j = Sa[Rank[i] - 1]; while (s[i + k] == s[j + k]) k++; Height[Rank[i]] = k; } } int main() { int i, n; while (scanf("%s", s) != EOF) { int len = strlen(s); s[len] = (int)('z') + 1; scanf("%s", s + len + 1); n = strlen(s),s[n] = 0; build_Sa(n + 1, MAX_CHAR);//注意调用n+1 getHeight(n); int ma = 0; for (i = 2; i <= n; i++) if ((Sa[i - 1]<len && Sa[i]>len) || (Sa[i - 1] > len && Sa[i] < len)) ma = max(ma, Height[i]); printf("%d\n", ma); } return 0; }