Description
给定串 S , T S,T S,T。求 T T T在 S S S中出现的最长子串。
定义一个串 B B B匹配与 A A A当且仅当 ∣ A ∣ = ∣ B ∣ |A|=|B| ∣A∣=∣B∣且至多存在一个 i i i满足 A i ≠ B i A_i \neq B_i Ai=Bi。
Solution
考虑枚举题目中的 i i i,即模糊匹配位置。
设它在 T T T串中的位置为 i i i,则要找到在 S S S串中的位置 j j j使得 l c p ( T [ i + 1 , m ] , S [ j + 1 , n ] ) + l c s ( S [ 1 , i − 1 ] , T [ 1 , j − 1 ] ) lcp(T[i+1,m],S[j+1,n])+lcs(S[1,i-1],T[1,j-1]) lcp(T[i+1,m],S[j+1,n])+lcs(S[1,i−1],T[1,j−1])。
分别建立前缀树和后缀树,那么题目等价于找两个点,使得它们在两棵树上的 l c a lca lca深度之和最大。
在第一棵树上 D F S DFS DFS,维护按第二棵树上的 d f s dfs dfs序排序的 s e t set set。每次合并时算一下答案。
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
inline void chkmax(int &a, int b) {if (a < b) a = b;}
const int maxn = 400005;
int n, m, ans;
char s[maxn], t[maxn];
struct SAM
{
int rt = 1, tot = 1, lst = 1, fa[maxn], Id[maxn], pos[maxn], len[maxn], ch[maxn][27];
void extend(int c, int i)
{
int pre = lst, x = ++tot;
len[x] = len[pre] + 1; Id[x] = i; pos[i] = lst = x;
while (pre && !ch[pre][c]) ch[pre][c] = x, pre = fa[pre];
if (!pre) fa[x] = rt;
else {
int k = ch[pre][c];
if (len[k] == len[pre] + 1) fa[x] = k;
else {
int y = ++tot;
memcpy(ch[y], ch[k], sizeof(ch[y]));
len[y] = len[pre] + 1; fa[y] = fa[k]; fa[k] = fa[x] = y;
while (pre && ch[pre][c] == k) ch[pre][c] = y, pre = fa[pre];
}
}
}
} sam1, sam2;
vector<int> to1[maxn], to2[maxn];
int Time, son[maxn], siz[maxn], top[maxn], dfn[maxn];
struct cmp {
bool operator() (int a, int b) {
return dfn[sam2.pos[sam1.Id[a] - 2]] <= dfn[sam2.pos[sam1.Id[b] - 2]];
}
};
set<int, cmp> tmp[maxn << 1];
set<int, cmp> *S[maxn], *T[maxn];
void dfs1(int u)
{
siz[u] = 1; dfn[u] = ++Time;
for (int v : to2[u]) {
dfs1(v);
siz[u] += siz[v];
if (siz[v] >= siz[son[u]]) son[u] = v;
}
}
void dfs2(int u)
{
if (son[u]) top[son[u]] = top[u], dfs2(son[u]);
for (int v : to2[u])
if (v != son[u]) top[v] = v, dfs2(v);
}
int lca(int u, int v)
{
while (top[u] != top[v]) {
if (sam2.len[top[u]] > sam2.len[top[v]]) u = sam2.fa[top[u]];
else v = sam2.fa[top[v]];
}
return sam2.len[u] <= sam2.len[v] ? u : v;
}
int dist(int u, int v)
{
if (min(sam1.Id[u], sam1.Id[v]) <= 1 || max(sam1.Id[u], sam1.Id[v]) <= n + 2) return 0;
return sam2.len[lca(sam2.pos[sam1.Id[u] - 2], sam2.pos[sam1.Id[v] - 2])] + 1;
}
void calc(set<int, cmp> *S, set<int, cmp> *T, int len)
{
bool flg = 0;
if (S -> size() > T -> size()) swap(S, T), flg = 1;
for (int u : (*S)) {
auto it = T -> lower_bound(u);
if (it != T -> begin()) --it, chkmax(ans, dist(u, *it) + len), ++it;
if (it != T -> end()) chkmax(ans, dist(u, *it) + len);
}
if (flg) swap(S, T);
}
void merge(set<int, cmp> *&S, set<int, cmp> *&T)
{
if (S -> size() < T -> size()) swap(S, T);
for (int u : (*T)) S -> insert(u);
}
void dfs(int u)
{
S[u] = &tmp[u * 2 - 1]; T[u] = &tmp[u * 2];
if (1 <= sam1.Id[u] && sam1.Id[u] <= n) S[u] -> insert(u);
else if (n + 1 < sam1.Id[u] && sam1.Id[u] <= n + m + 1) T[u] -> insert(u);
for (int v : to1[u]) {
dfs(v);
calc(S[u], T[v], sam1.len[u]);
calc(S[v], T[u], sam1.len[u]);
merge(S[u], S[v]);
merge(T[u], T[v]);
}
}
int main()
{
scanf("%s", s + 1); n = strlen(s + 1);
scanf("%s", t + 1); m = strlen(t + 1);
for (int i = m; i >= 1; --i) sam1.extend(t[i] - 'a', n + i + 1);
sam1.extend(26, n + 1);
for (int i = n; i >= 1; --i) sam1.extend(s[i] - 'a', i);
for (int i = 1; i <= n; ++i) sam2.extend(s[i] - 'a', i);
sam2.extend(26, n + 1);
for (int i = 1; i <= m; ++i) sam2.extend(t[i] - 'a', n + i + 1);
for (int i = 2; i <= sam1.tot; ++i) to1[sam1.fa[i]].push_back(i);
for (int i = 2; i <= sam2.tot; ++i) to2[sam2.fa[i]].push_back(i);
dfs1(1);
dfs2(1);
dfs(1);
printf("%d\n", ans);
return 0;
}