Clarkson
La solución tiene dos partes:
- Hallar el arreglo tal que la subcadena es una subcadena de y es maximal.
- Búsqueda binaria de la respuesta.
Parte 1 - Hallar
Esta es la parte más difícil del problema. La resolveremos con un arreglo de sufijos y LCP.
Primero, concatenamos y con un carácter ~ y construimos el arreglo de sufijos
de la cadena resultante. Con ese arreglo de sufijos construimos el arreglo LCP. (Si no se conoce esta técnica, ver
este curso de Codeforces .)
Recordemos que el LCP de dos sufijos es el mínimo de rango de los elementos del arreglo LCP entre esos dos sufijos.
Como queremos que sea maximal, solo hace falta revisar dos sufijos para
cada : ¡los dos sufijos más cercanos al sufijo que empieza en ! Podemos
hallar los índices de esos dos sufijos con un std::set.
Esta parte del algoritmo corre en tiempo (pero construcciones más lentas del arreglo de sufijos también pasan).
Parte 2 - Búsqueda binaria
La búsqueda binaria funciona porque si podemos lograr una longitud mínima , entonces también podemos lograr una longitud mínima (ya que no necesariamente necesitamos una subcadena de longitud ; solo necesitamos que las longitudes de todas las subcadenas sean de longitud al menos ).
Así comprobamos si podemos lograr una cierta longitud mínima :
Sea si podemos formar usando subcadenas de de tamaño al menos . Claramente, si es verdadero para algún , entonces también es verdadero.
Para comprobar si existe tal , podemos guardar todos los índices donde es verdadero en un conjunto y revisar si alguno cae en el rango .
Esta parte del algoritmo corre en tiempo (o si hacemos la comprobación en ).
Implementación
#include <bits/stdc++.h>
using namespace std;
string s, t, u;
int n, m, l;
int ord[200005], nord[200005], suff[200005], rev[200005], p = 1;
int lcp[200005][20], match[100000], h = 0;
set<int> topgear;
void build_suffix_arr() {
u = s + '~' + t;
l = n + m + 1;
ord[l] = -1;
for (int i = 0; i < l; i++) ord[i] = u[i], suff[i] = i;
auto cmp = [](int A, int B) {
if (ord[A] == ord[B]) return ord[A + p] < ord[B + p];
return ord[A] < ord[B];
};
while (p <= l) {
sort(suff, suff + l, cmp);
nord[suff[0]] = 0;
for (int i = 1; i < l; i++) {
nord[suff[i]] = nord[suff[i - 1]];
if (cmp(suff[i - 1], suff[i])) nord[suff[i]]++;
}
for (int i = 0; i < l; i++) ord[i] = nord[i];
p <<= 1;
}
for (int i = 0; i < l; i++) rev[suff[i]] = i;
for (int i = 0; i < l; i++) {
if (rev[i]) {
int j = suff[rev[i] - 1];
while (u[i + h] == u[j + h]) h++;
lcp[rev[i]][0] = h;
}
h = max(h - 1, 0);
}
for (int i = n + 1; i < l; i++) topgear.insert(rev[i]);
for (int j = 1; j < 20; j++) {
for (int i = 0; i <= l - (1 << j); i++) {
lcp[i][j] = min(lcp[i][j - 1], lcp[i + (1 << j - 1)][j - 1]);
}
}
}
int rmq(int l, int r) {
int level = 31 - __builtin_clz(r - l + 1);
return min(lcp[l][level], lcp[r - (1 << level) + 1][level]);
}
bool check(int len) {
set<int> good;
good.insert(n);
for (int i = n - 1; ~i; i--) {
set<int>::iterator lb = good.lower_bound(i + len);
if (lb != good.end() && *lb <= i + match[i]) good.insert(i);
}
return !*good.begin();
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cin >> s >> t;
n = s.size(), m = t.size();
build_suffix_arr();
for (int i = 0; i < n; i++) {
set<int>::iterator lb = topgear.lower_bound(rev[i]);
if (lb != topgear.end()) match[i] = max(match[i], rmq(rev[i] + 1, *lb));
if (lb != topgear.begin()) {
lb--;
match[i] = max(match[i], rmq(*lb + 1, rev[i]));
}
}
int l = 0, r = n;
while (l != r) {
int mid = (l + r + 1) / 2;
if (check(mid)) l = mid;
else r = mid - 1;
}
cout << (l ? l : -1);
return 0;
}