首页 > 其他分享 >扩展 KMP/exKMP(Z 函数)

扩展 KMP/exKMP(Z 函数)

时间:2023-12-21 19:13:55浏览次数:26  
标签:exKMP 函数 LCP int 后缀 KMP lne

模板链接 QwQ

Z 函数,又称扩展 KMP (exkmp),可以 \(O(n)\) 求出一个字符串的所有后缀与这个字符串的 LCP 长度。

怎么叫做扩展 KMP 但是前置知识没有 KMP,Z 函数的做法与 Manacher 有着异曲同工之妙,即存下了目前已扩展到的右端点最靠右端的后缀 \(i\) 与原串的 LCP:\([i,i+Z[i]-1]\),可以发现,当 \(i<j\leq i+Z[i]-1\) 时,字符串 \([j,i+Z[i]-1]\) 与 \([j-i+1,z[i]]\)(相当于将 \([1,Z[i]]\)、\([i,i+Z[i]-1]\) 按 \(j\) 对应的位置切开取了右半边) 一定是相同的,那么 \(Z[j]\) 就可以直接初赋值上 \(\min\{Z[j-i+1],i+Z[i]-j\}\),这样就能保证每个字符只会被扩展到 \(1\) 次,因此总时间复杂度是 \(O(n)\) 的(纯意识流是因为懒得画图嘿嘿)

于是我们可以在 \(O(|a|+|b|)\) 的时间中求模式串 \(b\) 与另一个字符串 \(a\) 所有后缀的 LCP 长度,具体的,可以先求出 \(b\) 的 Z 函数后参照求 Z 函数的思路求 \(a\) 的所有后缀与 \(b\) 的 LCP,也可以将 \(b\)、\(a\) 拼在一起后求一次 Z 函数,模板代码如下:(此处只展示第一种先求 \(b\) 的 Z 函数的做法 QwQ,毕竟会求 Z 函数就会第二种求法了)

#include <bits/stdc++.h>
using namespace std;
const int N = 2e7+5;
int n, m, z[N], p[N];
char a[N], b[N];

template <typename T> void read(T& x) {
	x = 0; int f = 0; char c = getchar();
	while(c < '0' || c > '9') f |= (c == '-'), c=getchar();
	while(c >= '0' && c <= '9') x=(x<<1)+(x<<3)+(c^48), c=getchar();
	x=(f ? -x : x);
}
int lne; char put[105];
template <typename T> void write(T x, char ch) {
	lne = 0; if(x < 0) putchar('-'), x=-x;
	do { put[++lne]=x%10, x/=10; } while(x);
	while(lne) putchar(put[lne--]^48);
	putchar(ch);
}

signed main() {
	scanf("%s%s", a+1, b+1);
    n=strlen(a+1), m=strlen(b+1);
    //求Z函数:
    z[1]=m;//初赋值
    for(int i = 2, l = 0, r = 0; i <= m; ++i) {
        if(i <= r) z[i]=min(z[i-l+1], r-i+1);//与Manacher异曲同工之处
        while(i+z[i] <= m && b[i+z[i]] == b[z[i]+1]) ++z[i];//向右扩展没有被扩展过的位置
        if(i+z[i]-1 > r) r=i+z[i]-1, l=i;//更新
    }
    //求a的后缀与b的LCP长度:
    for(int i = 1/*从1开始,求Z函数时则不能从1开始*/, l = 0, r = 0; i <= n; ++i) {
        if(i <= r) p[i]=min(z[i-l+1], r-i+1);//是对b求得的Z函数取min!一定要注意
        while(i+p[i] <= n && a[i+p[i]] == b[p[i]+1]) ++p[i];//a与b比较
        if(i+p[i]-1 > r) r=i+p[i]-1, l=i;//更新
    }
    long long ans1 = 0, ans2 = 0;
    for(int i = 1; i <= m; ++i)
        ans1^=1LL*i*(z[i]+1);
    for(int i = 1; i <= n; ++i)
        ans2^=1LL*i*(p[i]+1);
    write(ans1, '\n');
    write(ans2, '\n');
	return 0;
}

看着似乎用处不大可以被二分加哈希代替但是在有时不能二分哈希或者卡常的时候还是很有用的

标签:exKMP,函数,LCP,int,后缀,KMP,lne
From: https://www.cnblogs.com/RJlalala/p/ex-KMP.html

相关文章

  • Python代码中的偏函数
    技术背景在数学中我们都学过偏导数\(\frac{\partialf(x,y)}{\partialx}\),而这里我们提到的偏函数,指的是\(f(y)(x)\)。也就是说,在代码实现的过程中,虽然我们实现的一个函数可能带有很多个变量,但是可以用偏函数的形式把其中一些不需要拆分和变化的变量转变为固有变量。比较典型的......
  • 无涯教程-Go - 函数指针
    Go编程语言使您可以将指针传递给函数,只需将函数参数声明为指针类型。在下面的示例中,我们将两个指针传递给一个函数,并更改该函数内部的值,该值会反映在调用函数中-packagemainimport"fmt"funcmain(){/*局部变量定义*/varaint=100varbint=200fmt.P......
  • 为什么在Python类中经常会使用init函数
     在Python中,类是一种用于创建对象的蓝图或模板。当我们定义一个类时,经常会在类中定义一个名为`__init__`的函数,也称为构造函数或初始化方法。本文将解释为什么在Python类中经常会使用`__init__`函数,并介绍它的作用和用法。 1.初始化对象: `__init__`函数在创建类的对象时自动调......
  • 在 MySQL 中,你可以使用 `AVG()` 函数来计算一组值或表达式的平均值。`AVG()` 函数的基
    在MySQL中,AVG()函数在计算平均值时会自动忽略NULL值¹⁴。也就是说,它只会计算所有非空值的平均值³。例如,假设你有一个包含以下值的列:90,80,70,85,95,NULL,NULL。在这种情况下,AVG()函数将只计算非空值的平均值,即:而不是将NULL值视为0并计算所有值的平均值。如果你需......
  • 第12次上机内容 函数
    1、阅读程序(1)作用:打印a的值。分析结果:38。运行结果:考察函数传值,不是经典转换ab值不是很认可。(2)说什么来什么,不过很没必要省着点空间吧?作用:打印x、y的值或者应该是交换x、y的值未果,证明函数传值不修改变量值。分析结果3,55,3运行结果......
  • 无涯教程-Go - 多维数组函数
    Go编程语言允许多维数组,这是多维数组声明的一般形式-varvariable_name[SIZE1][SIZE2]...[SIZEN]variable_type如,以下声明创建了三维5、10、4个整数数组-varthreedim[5][10][4]int二维数组二维数组是多维数组的最简单形式,本质上,二维数组是一维数组的列表,要声明大小为[x......
  • Odoo中防止用户同一时间多次点击同一按钮触发函数
    我们将探讨如何在Odoo中实现一个全局防重复点击功能,以防止用户在短时间内重复点击按钮而触发多次函数调用。这种情况通常发生在用户不断快速点击同一个按钮时,导致后端函数被多次调用,可能会引起数据错误或性能问题。在Odoo中,我们可以通过自定义模块来实现这个功能。首先,我们需要在bu......
  • 无涯教程-Go - 函数闭包
    Go编程语言支持可以充当函数闭包的匿名函数,当我们要内联定义函数而不传递任何名称时,将使用匿名函数。在我们的示例中,我们创建了一个函数getSequence(),该函数返回另一个函数,此函数的目的是关闭上层函数的变量i形成闭包。如-packagemainimport"fmt"funcgetSequence()func......
  • 无涯教程-Go - Function as Value函数
    在下面的示例中,我们使用函数定义初始化了一个变量,该函数变量的目的只是使用内置的math.sqrt()函数。如-packagemainimport("fmt""math")funcmain(){/*声明一个函数变量*/getSquareRoot:=func(xfloat64)float64{returnmath.Sqrt(x)}/*......
  • 调用并调试函数
    debug命令可以调用并调试一个函数在我们想要查找问题并进行详细调试的时候,一个简单的技巧就是先调用一下 debugger 命令。例如,假设我们有以下形式的函数:functionfn(){/*某些代码*/}可以在自己的控制台里这样操作:debugger;fn(1);然后点击 Stepintonextfunc......