眉目舒展,顺问冬安kiritokazuto

Z函数 (扩展KMP)

kiritokazuto·2022-11-09 20:06·164 次阅读

Z函数 (扩展KMP)

Z函数 (扩展KMP)

  • 这里只是口胡一下一些重要的点,想看图理解的可以去这里。我就是看这个题解学会的。

  • 这里先留一个疑问,也是本篇博客所要解决的最大的问题,那就是第一种情况为什么是 i+l<=r 而非 i+l1<=r

定义

  • z[i] 表示 以 i 为起点的后缀和原串的LCP

  • 首先考虑求 iz 函数,假设现在已经求完了0i1的所有了。那么我们时刻维护一个 kr分别表示现在已知的所有z函数里能扩展到的最远范围(除去z[0]), kr对应的左端点。

  • 那么我们可以显然地发现[0,z[k]1][k,r]是相同的,同理[0,ik][k,i]是相同的,我们设 l表示z[ik], 那么我们发现[0,l1][ik,ik+l1]是想同的(因为l就是ikz), 所以可以得到[ik,ik+l1][i,i+l1]是相同的,又因为我z中存的都是最大的,所以l就是我z[i]的一个候选项。

  • 第一种情况i+l1<r也就是i+lr注意这里就是我开头问的问题,也就是第一个不等式为什么不取等。

    • 当然首先,这种情况下显然我的z[i]=l。因为在r以内,不可能再大。
    • 我们考虑,现在的r仅仅是我已知的最远距离而可能不是真正的最远距离,那么如果我在第一个不等式挂上等,相当于是强制现在的z[i]等于l,不再给他机会向外扩展,但我的z[i]能到l是因为你现在给我的最大范围是l而不是我真正只能匹配到l,也就是说我在现有的范围里已经完全匹配上了,到达边界r了,我还应该继续向外扩展尝试,我是否能匹配更多,而不是停下,但是对于其他的无法触碰到r的情况,我最大也就匹配成这样了,所以直接赋值是对的。
  • 第二种情况就是i+l>r了,显然我直接从max(0,ri+1)开始暴力匹配就好了,复杂度不会退化,因为对于内层用于匹配的while循环,每次执行r最少加一,总共执行n次,对于外层的for是线性的遍历,所以总复杂度是O(n)的。

    • 当然,这种情况更新之后,我的i一定是一个更加优秀的k记得更新,否则会TLE退化成O(n2)

Code

here
Copy
#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 @   kiritokazuto  阅读(164)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示