HDU 6138 Fleet of the Eternal Throne (扩展KMP)
Description
给出\(n\)个字符串\(s_i\)和\(m\)个查询,每个查询询给出两个下标\(x\)和\(y\),\(s_x\)和\(s_y\)的最长公共子串的长度,并且这个公共子串还要求是这\(n\)个字符串中的某个字符串的前缀,包括\(s_x\)和\(s_y\)。
Input
第一行给出用例组数\(T\)。
对于每组用例,第一行给出字符串数量\(n\),接下来\(n\)行,每行给出一个字符串。\(n \leqslant 10^5\),\(\sum{|s_i|} \leqslant 10^5\)。
接下来给出询问数量\(m\),接下来\(m\)行,每行给出两个整数\(x\)和\(y\),询问第\(x\)和第\(y\)个子串。字符串编号从\(1\)到\(n\)。\(m \leqslant 100\)。
Output
对于每个查询,输出一个整数表示这个查询的答案。
Sample Input
1
3
aaa
baaa
caaa
2
2 3
1 2
Sample Output
3
3
Solution
对于每个查询,枚举每个字符串\(s_i\),计算\(s_x\)和\(s_y\)是\(s_i\)的前缀的最长公共子串的长度,求最大值即可。
用扩展KMP算法计算最长公共子串,设以\(s_i\)为模式串在文本串\(s_x\)中匹配得到\(ext_x[]\)数组,\(ext_x[j]\)表示\(s_x\)串从\(j\)位置开始能够与\(s_i\)匹配到的最大长度,\(s_y\)同理,那么最终答案就是
\[\max_{1 \leqslant i \leqslant n}\{\min\{\max_{0 \leqslant j < |s_x|}\{ext_x[j]\}, \max_{0 \leqslant j < |s_y|}\{ext_y[j]\}\}\}
\]
Code
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 10;
string s[N];
int nxt[N], ext[N];
void pre_kmp(string pat)
{
int lpat = pat.length();
nxt[0] = lpat;
int j = 0;
while (j + 1 < lpat && pat[j] == pat[j + 1]) j++;
nxt[1] = j;
for (int i = 2, k = 1; i < lpat; i++)
{
int p = nxt[k] + k - 1;
int l = nxt[i - k];
if (i + l < p + 1) nxt[i] = l;
else
{
j = max(0, p - i + 1);
while (i + j < lpat && pat[i + j] == pat[j]) j++;
nxt[i] = j;
k = i;
}
}
}
void ext_kmp(string txt, string pat)
{
int ltxt = txt.length();
int lpat = pat.length();
int j = 0;
while (j < ltxt && j < lpat && pat[j] == txt[j]) j++;
ext[0] = j;
for (int i = 1, k = 0; i < ltxt; i++)
{
int p = ext[k] + k - 1;
int l = nxt[i - k];
if (i + l < p + 1) ext[i] = l;
else
{
j = max(0, p - i + 1);
while (i + j < ltxt && j < lpat && txt[i + j] == pat[j]) j++;
ext[i] = j;
k = i;
}
}
}
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) cin >> s[i];
int m;
scanf("%d", &m);
while (m--)
{
int x, y;
scanf("%d%d", &x, &y);
int ans = 0;
for (int i = 1; i <= n; i++)
{
pre_kmp(s[i]);
ext_kmp(s[x], s[i]);
int maxx = 0;
for (int j = 0; j < s[x].length(); j++) maxx = max(maxx, ext[j]);
ext_kmp(s[y], s[i]);
int maxy = 0;
for (int j = 0; j < s[y].length(); j++) maxy = max(maxy, ext[j]);
ans = max(ans, min(maxx, maxy));
}
printf("%d\n", ans);
}
}
return 0;
}