Z函数 (扩展KMP)
Z函数 (扩展KMP)
-
这里只是口胡一下一些重要的点,想看图理解的可以去这里。我就是看这个题解学会的。
-
这里先留一个疑问,也是本篇博客所要解决的最大的问题,那就是第一种情况为什么是 \(i + l <= r\) 而非 \(i + l - 1 <= r\)
定义
-
\(z[i]\) 表示 以 \(i\) 为起点的后缀和原串的\(LCP\)
-
首先考虑求 \(i\) 的 \(z\) 函数,假设现在已经求完了\(0 \sim i - 1\)的所有了。那么我们时刻维护一个 \(k\) 和 \(r\)分别表示现在已知的所有\(z\)函数里能扩展到的最远范围(除去\(z[0]\)), \(k\) 为\(r\)对应的左端点。
-
那么我们可以显然地发现\([0, z[k] - 1]\)和\([k, r]\)是相同的,同理\([0, i - k]\) 和 \([k, i]\)是相同的,我们设 \(l\)表示\(z[i - k]\), 那么我们发现\([0, l - 1]\)和\([i - k, i - k + l - 1]\)是想同的(因为\(l\)就是\(i - k\)的\(z\)), 所以可以得到\([i - k, i - k + l - 1]\)和\([i, i + l - 1]\)是相同的,又因为我\(z\)中存的都是最大的,所以\(l\)就是我\(z[i]\)的一个候选项。
-
第一种情况\(i + l - 1 < r\)也就是\(i + l \le r\)注意这里就是我开头问的问题,也就是第一个不等式为什么不取等。
- 当然首先,这种情况下显然我的\(z[i] = l\)。因为在\(r\)以内,不可能再大。
- 我们考虑,现在的\(r\)仅仅是我已知的最远距离而可能不是真正的最远距离,那么如果我在第一个不等式挂上等,相当于是强制现在的\(z[i]\)等于\(l\),不再给他机会向外扩展,但我的\(z[i]\)能到\(l\)是因为你现在给我的最大范围是\(l\)而不是我真正只能匹配到\(l\),也就是说我在现有的范围里已经完全匹配上了,到达边界\(r\)了,我还应该继续向外扩展尝试,我是否能匹配更多,而不是停下,但是对于其他的无法触碰到\(r\)的情况,我最大也就匹配成这样了,所以直接赋值是对的。
-
第二种情况就是\(i + l > r\)了,显然我直接从\(max(0, r - i + 1)\)开始暴力匹配就好了,复杂度不会退化,因为对于内层用于匹配的\(while\)循环,每次执行\(r\)最少加一,总共执行\(n\)次,对于外层的\(for\)是线性的遍历,所以总复杂度是\(O(n)\)的。
- 当然,这种情况更新之后,我的\(i\)一定是一个更加优秀的\(k\)记得更新,否则会\(TLE\)退化成\(O(n ^ 2)\)
Code
here
#include <bits/stdc++.h>
#define Re register int
#define LL long long
#define LD double
#define frein(x) freopen(#x ".in", "r", stdin)
#define freout(x) freopen(#x ".out", "w", stdout)
#define fr(x, y, z) for(Re x = y; x <= z; x ++)
#define fp(x, y, z) for(Re x = y; x >= z; x --)
#define delfr(x, y, z) for(Re x = y; x < z; x ++)
#define delfp(x, y, z) for(Re x = y; x > z; x --)
#define ki putchar(10)
#define fk putchar(' ')
#define mes(x, y) memset(x, y, sizeof(x))
#define fuc(x, y) inline x y
#define WMX aiaiaiai~~
using namespace std;
namespace kiritokazuto {
fuc(char, getc)(){
static char buf[1 << 18], *p1, *p2;
if(p1 == p2){
p1 = buf, p2 = buf + fread(buf, 1, 1 << 18, stdin);
if(p1 == p2)return EOF;
}
return *p1++;
}
fuc(LL, read)() {
LL x = 0, f = 1;char c = getc();
while(!isdigit(c)){if(c == '-')f = -1; c = getc();}
while(isdigit(c)){x = (x << 1) + (x << 3) + (c ^ 48); c = getc();}
return x * f;
}
template <typename T> fuc(void, write)(T x){
if(x < 0)putchar('-'), x = -x;
if(x > 9)write(x / 10);putchar(x % 10 | '0');
}
}
using namespace kiritokazuto;
const int maxn = 2e7 + 50000, Inf = 200000000, Mod = 998244353, lim = 2e5 ;
LL z[maxn], ext[maxn];
char t[maxn], s[maxn];
fuc(void, getz)(char *s) {
LL len = strlen(s);
LL k = 1, r = 0, l = 0;//0为本身,所以从1
z[0] = len;
while(r + 1 < len && s[r] == s[k + r])r++;//初始值
z[1] = r;
delfr(i, 2,len) {//1处理完,所以是二
r = k + z[k] - 1;//最远边界
l = z[i - k];
if(i + l <= r) z[i] = l;
else {
LL j = max(0 * 1ll, r - i + 1);
while(j + 1 < len && s[i + j] == s[j])j++;
z[i] = j;
k = i;
}
}
// delfr(i, 0, len) {
// printf("nxt[%d] = %d\n", i, z[i]);
// }
}
fuc(void, getext)(char *s, char *t) {
LL n = strlen(s), m = strlen(t);
LL r = 0, l = 0, k = 0;
while(r < n && r < m && t[r] == s[r])r++;
ext[0] = r;
delfr(i, 1, m) {
l = z[i - k];
r = k + ext[k] - 1;
if(i + l <= r)ext[i] = l;
else {
LL j = max(0 * 1ll, r - i + 1);
while(j < n && i + j < m && t[i + j] == s[j])j++;
ext[i] = j;
k = i;
}
}
// delfr(i, 0, m) {
// printf("ext[%d] = %d\n", i, ext[i]);
// }
}
LL ans = 0;
signed main() {
scanf("%s %s", t, s);
getz(s);
getext(s, t);
LL n = strlen(s), m = strlen(t);
delfr(i, 0, n) ans = (ans ^ (i + 1) * (z[i] + 1));
write(ans), ki;
ans = 0;
delfr(i, 0, m) ans = (ans ^ (i + 1) * (ext[i] + 1));
write(ans);
return 0;
}