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;
}

posted @ 2022-11-09 20:06  kiritokazuto  阅读(157)  评论(2编辑  收藏  举报