首页 > 其他分享 >KMP 与 Z 函数拓展

KMP 与 Z 函数拓展

时间:2024-08-05 10:07:52浏览次数:9  
标签:nxt 函数 root 拓展 KMP border 周期 前缀

【失配树:KMP 拓展】

先 KMP 一遍。然后对 \(0\sim n\) 建立一棵树:\(nxt[i]\) 作为 \(i\) 的父结点。

则最长公共 border 就是这棵树上的 LCA 对应的长度。


border:若 \(a\) 既是 \(s\) 的前缀又是 \(s\) 的后缀,则 \(a\) 是 \(s\) 的 border。

周期:若 \(s\) 以 \(p\) 为周期,则 \(s[i]=s[i\bmod p]\)。

整周期:若 \(s\) 以 \(p\) 为整周期,则 \(s\) 以 \(p\) 为周期且 \(p\mid n\)。

KMP 求出来的是 \(nxt\) 数组:\(nxt[i]\) 表示 \(s\) 的前 \(i\) 个字符构成的前缀的最长真 border 长度。

exKMP 求出来的是 \(Z\) 数组:\(z[i]\) 表示 \(s[0\sim n-1]\) 与 \(s[i\sim n-1]\) 的 LCP 长度。

记 \(R(s)\) 表示 \(s\) 无限循环构成的字符串。

记 \(root(s)\) 表示 \(s\) 的最小整周期字符串。

\(s\) 的 cyclic-shift 指 \(s\) 的循环移位。

【一些结论】

  1. 字符串 \(s\) 的所有 border 长度为 \(nxt[n],nxt[nxt[n]],\dots\)

  2. 若 \(s\) 有长度为 \(x\) 的 border \(\iff\) \(s\) 以 \(n-x\) 为周期。

  3. 如果 border 存在,最短 border 长度 \(\le n/2\)。

  4. 若 \(s\) 以 \(p\) 为整周期 \(\iff\) \(z[p]=n-p\)。

  5. \(|root(s)|<|s|\iff\) \(s\) 某个真 cyclic-shift 等于 \(s\)。

  6. 若 \((n-nxt[n])\mid n\),则 \(|root(s)|=n-nxt[n]\);否则 \(root(s)=s\)。

  7. 若 \(s\) 分别以 \(p,q\) 为整周期,则 \(s\) 以 \(gcd(p,q)\) 为整周期。(弱周期定理)

    证明:考虑 \(A=R(s)\)。有 \(A_i=A_{i+p}=A_{i+q}\)。由裴蜀定理,存在 \(ap+bq=gcd(p,q)\)。则 \(A_i=A_{i+ap+bq}=A_{i+gcd(p,q)}\)。

  8. \(R(a)=R(b)\iff ab=ba\)。

    考虑引入第三个命题:\(root(a)=root(b)\)。我们尝试证明 \(R(a)=R(b)\) 和 \(ab=ba\) 都与 \(root(a)=root(b)\) 等价。显然从 \(root(a)=root(b)\) 去推另外两个是很简单的。

    1. \(R(a)=R(b)\Rightarrow root(a)=root(b)\)。

      由结论 7,\(R(a)\) 以 \(gcd(|a|,|b|)\) 为周期,则 \(a,b\) 都以 \(R(a)\) 的前 \(gcd(|a|,|b|)\) 个字符作为周期。

    2. \(ab=ba\Rightarrow root(a)=root(b)\)。

      根据 \(|a|+|b|\) 的长度归纳。显然 \(|a|+|b|=2\) 的时候成立。然后考虑 \(|a|>|b|,|a|=|b|,|a|<|b|\) 三种情况。\(|a|=|b|\) 显然。而 \(|a|<|b|\) 可以对称。因此只需要证明 \(|a|>|b|\) 情况。

      当 \(|a|>|b|\),\(ab=ba\)。不妨 \(a=bc\),则 \(bcb=bbc\)。于是 \(cb=bc\),由归纳法知 \(root(b)=root(c)\),而 \(a=bc\) 显然 \(root\) 也相等。

    于是证毕。

  9. \(R(a)<R(b)\iff ab<ba\)。

    考虑 \(\sum=\{0\sim 9\}\) 的情况(看作十进制数)。记 \(n=|a|,m=|b|\)。

    左边可以看作两个循环小数的比较:\(\overline{0.aaaa...}<\overline{0.bbbb....}\iff a\times \dfrac{1}{10^n-1}<b\times \dfrac{1}{10^m-1}\)。

    右边可以写成十进制数:\(a\times 10^m+b<b\times 10^n+a\)。

    可以发现这两个式子等价。拓展到更大的字符集也可以类似做,只是进制改变了。

【题目】

  • 判断两个字符串 \(s1,s2\) 是否循环同构。

    令 \(s=s1s1\),判断 \(s\) 中是否有 \(s2\) 即可。跑一遍 KMP。

  • Periods of Words

    题意:若 \(a\) 是 \(s\) 的真前缀,且 \(s\) 是 \(aa\) 的前缀,称 \(a\) 是 \(s\) 的周期。给定字符串 \(w\),求 \(w\) 每个前缀的最长周期长度之和(如果不存在算 \(0\))。注意这个周期的定义和上面不是一个。

    容易发现,对于一个固定的 \(s\),它最大周期等于 \(|s|\) 减去 \(s\) 的最短真 border 的长度(如果不存在真 border 则不存在周期)。

    因此跑一遍 KMP,可以求出每个前缀原始的 \(nxt\)。这里又有两种方法。

    1. 失配树找最高级祖先。

    2. 类似并查集路径压缩。因为如果当前位置的 \(nxt\) 不是这个位置最小的 \(nxt\),那么任何时刻都不会用到这个位置的 \(nxt\)。于是每到一个新位置,直接暴力把当前位置的 \(nxt\) 一直递归到最小即可。

  • 寻找 \(t\) 的最长前缀是 \(s\) 的子串。

    SAM 直接冲

    我们可以用 KMP 这个优雅的算法。令 \(t\) 作为模式串,\(s\) 作为母串。

    观察得知,KMP 当匹配到母串的第 \(i\) 个位置时,实际上已经算出了 \(t\) 作为 \(s[1\sim i]\) 的后缀的最大前缀 \(rho[i]\)。

    答案就是 \(\max\{rho[i]\}\).

  • 求 \(s\) 每个前缀的出现次数。

    SAM 直接冲

    1. KMP 方法。先对 \(s\) 做一遍 KMP。对于前缀 \(s[1\sim i]\),只要看 \(nxt[i+1\sim n]\) 里有多少个 \(nxt=i\) 即可。不要忘了加上自己开头出现了一次。

    2. Z 函数方法。先求出 \(Z\) 函数。对于前缀 \(s[1\sim i]\),只要看 \(z[2\sim n]\) 里有多少个 \(z\ge i\) 即可。不要忘了加上自己开头出现了一次。

  • Extend to Palindrome

    简化题意:求最长回文后缀。

    令 \(t=rev(s)+\text{#}+s\)。对 \(t\) 求 \(Z\) 函数,然后枚举每个后缀判断即可。

  • 求本质不同子串数。

    SAM 直接冲

    考虑增量算法:已经处理了字符串 \(s\),在前面加一个字符 \(c\),考虑以 \(c\) 开头的子串们。利用 \(nxt[]/z[]\) 去重。

    不过因为每新加入一个字符就要重新求一遍,复杂度 \(O(n^2)\)。

  • Pal-Palindrome

    结论:两个回文串 \(a,b\)。\(ab\in Pal\iff root(a)=root(b)\)。

    那么弄一个类似桶的东西即可。

标签:nxt,函数,root,拓展,KMP,border,周期,前缀
From: https://www.cnblogs.com/FLY-lai/p/18340658

相关文章

  • excel函数的学习
    1、学习excelSUM :求和函数AVERAGE:平均值函数IFROUNDMAXMININTVLOOKUPSUMIFSUMSIFCOUNTCOUNTIFNOWTODAYMIDPHONETICLENRIGHT二、实操(1)SUM :求和函数 条件判断函数四舍五入函数最大值函数最小值函数数据取整函数条件查找函数按条件求和函数按多个条件求和函数统计数字个......
  • 尝试从函数内部更改值,但退出函数时它不会改变
    尝试制作一个准系统的Pokemon游戏并且切换不起作用当尝试切换Pokemon时,我可以让代码识别activePlayerMon=在运行switchOption()函数时有一个值切换,但是一旦调试器离开该函数,它就会恢复当第一次提示您选择Mon时,返回到最初给定的值deffight(playerlist,computerl......
  • 字符专用输入输出函数 getchar() putchar()
    文章目录一、字符专用接收函数1.1scanf实现字符接收1.2字符专用接收函数getchar1.3练习1.4利用循环使字符接收函数接收字符串的元素二、字符专用输出函数2.1printf实现打印字符2.2字符专用输出函数putchar提示:以下是本篇文章正文内容,下面案例可供参考一、字......
  • KMP算法
     ......
  • KMP算法
    写在前面喜报:听了四遍都没学懂的KMP算法,终于在gyy大佬的耐心讲解下搞懂了,大佬orz!!!正文kmp算法本质上就是对模式串(要匹配的子串两个串中短的那个)中很多重复的前缀和后缀索引起来,使得在一个地方失配了也不要紧,不用重新来的算法(看不懂不要紧)下面我们就来详细介绍一下kmp的几个......
  • KMP学习笔记
    KMP一种字符串单模匹配算法。原理当模式串\(s\)与文本串\(t\)进行匹配时,容易想到的一种朴素做法就是将模式串的第一位与文本串的每一位进行试配。但是这样效率过低,容易被数据卡成\(O(n^2)\)。KMP单模匹配算法引入了一个失配数组border。定义一个字符串的border为一......
  • 深入理解 ReLU 激活函数及其在深度学习中的应用【激活函数、Sigmoid、Tanh】
    ReLU(RectifiedLinearUnit)激活函数ReLU(RectifiedLinearUnit)激活函数是一种广泛应用于神经网络中的非线性激活函数。其公式如下:ReLU(x......
  • C语言学习----常用函数
    1.输入输出:scanf输入printf输出格式:scanf("格式控制符",变量的地址);printf(“格式控制符”,变量);注意变量的地址和变量不同,变量的地址用取址符&加变量名组成例如&a;inta;scanf("%d",&a);printf("%d",a);这段代码会要求从控制台输入一个整数,然后输出它。格式控制......
  • 如何为可以在递归调用中重新分配的 python 函数制定类型提示?
    采取以下最小示例:S=TypeVar("S",bound=int|str)defmeth(a:S)->S:ifa=="5":returnstr(meth(int(a)))returna特别是,上面的方法可以采用字符串或整数。它总是返回与其输入相同类型的值,但它可以递归地调用自身,在这种情况下,S的值......
  • KMP 算法学习笔记
    问题引入给出两个字符串\(s1\)和\(s2\),求出\(s2\)在\(s1\)中所有出现的位置(出现指\(s1\)中存在子串与\(s2\)完全相同)。朴素暴力不详细介绍,容易发现时间复杂度不优秀。KMP算法思想在朴素暴力中我们可以发现有很多匹配是不需要再次从头开始重新匹配的,举个例子:ABA......