首页 > 其他分享 >「学习笔记」Z 函数(扩展 KMP)

「学习笔记」Z 函数(扩展 KMP)

时间:2022-08-18 11:12:02浏览次数:81  
标签:函数 int 扩展 笔记 算法 KMP sim

前置芝士:KMP 算法

正文

本文涉及的字符串下标以 \(0\) 为起点。

对于个长度为 \(n\) 的字符串 \(s\)。定义函数 \(z(i)\) 表示 \(s\) 和 \(s_{i\sim n-1}\)(即以 \(s_i\) 开头的后缀)的最长公共前缀(LCP)的长度。\(z\) 被称为 \(s\) 的 Z 函数

特别地,\(z(0)=0\)。

所谓的 Z 函数其实就是国内 OIer 所说的 “扩展 KMP”

求解 Z 函数的值有一个朴素算法,即直接按照 Z 函数的定义暴力求解:

inline vector<int> z_function_trivial(string s)
{
	int n=(int)s.size();
	vector<int>z(n);
	rep(i,1,n-1)
	{
		while(i+z[i]<n&&s[z[i]]==s[i+z[i]])
			++z[i];
	}
	return z;
}

显然,这样的时间复杂度是 \(O(n^2)\) 的。

但是这样的算法速度还是太慢了,我们考虑优化。

而优化的关键就在于,运用自动机的思想寻找限制条件下的状态转移函数,使得可以借助之前的状态来加速计算新的状态。

在该算法中,我们从 \(1\) 到 \(n-1\) 顺次计算 \(z(i)\) 的值(\(z(0)=0\))。

在计算 \(z(i)\) 的过程中,我们会利用已经计算好的 \(z(0),z(1),\cdots,z(i-2)\)。

对于 \(i\),我们称区间 \([i,i+z(i)-1]\) 是 \(i\) 的 匹配段,也可以叫作 Z-box

算法的过程中我们维护右端点最靠右的匹配段。

为了方便,记作 \([l,r]\)。根据定义,\(s_{l\sim r}\) 是 \(s\) 的前缀。

在计算 \(z(i)\) 时我们保证 \(l\le i\)。初始时 \(l=r=0\)。

在计算 \(z(i)\) 的过程中:

  • 如果 \(i\le r\),那么根据 \([l,r]\) 的定义有 \(s_{i\sim r}=s_{i-l\sim r-l}\),因此 \(z(i)\ge\min(z(i-l),r-l+1)\)。这时:
    • 若 \(z(i-l)<r-l+1\),则 \(z(i)=z(i-l)\)。
    • 否则 \(z(i-l)\ge r-l+1\),这时我们令 \(z(i)=r-l+1\),然后暴力枚举下一个字符扩展 \(z(i)\) 直到不能扩展为止。
  • 如果 \(i>r\),那么我们直接按照朴素算法,从 \(s_i\) 开始比较,暴力求出 \(z(i)\)。
  • 在求出 \(z(i)\) 后,如果 \(i+z(i)-1>r\),我们就需要更新 \([l,r]\),即令 \(l\gets i,r\gets i+z(i)-1\)。

可以访问 这个网站 来看 Z 函数的模拟过程。

inline vector<int> z_function(string s)
{
	int n=(int)s.size();
	vector<int>z(n);
	int l=0,r=0;
	rep(i,1,n-1)
	{
		if(i<=r&&z[i-l]<r-i+1)
			z[i]=z[i-l];
		else
		{
			z[i]=max(0,r-i+1);
			while(i+z[i]<n&&s[z[i]]==s[i+z[i]])
				++z[i];
		}
		if(i+z[i]-1>r)
		{
			l=i;
			r=i+z[i]-1;
		}
	}
	return z;
}

对于内层 while 循环,每次执行都会使得 \(r\) 向后移至少 \(1\) 位,而 \(r<n-1\),所以总共只会执行 \(n\) 次。

对于外层循环,只有一遍线性遍历。

总时间复杂度为 \(O(n)\)。

END.

标签:函数,int,扩展,笔记,算法,KMP,sim
From: https://www.cnblogs.com/cantorsort2919/p/16597727.html

相关文章

  • Vue学习笔记4-项目开发规范及插件
    Vue学习笔记4-项目开发规范及插件一、安装插件首先搜索安装ESLint和Prettier这两个插件。这里对开发规范的配置仅配置ESLint,对代码格式的配置仅配置Prettier,用于代......
  • 《伯恩斯焦虑自助疗法》读书笔记3
      每日情绪日志       基于逻辑的语义疗法,包括过程结果法和语义法。  过程结果法:你可以根据你投入的努力过程,来评价自己的结果,过程和结果同样重要。......
  • 部分功能实现笔记
    部分功能实现笔记以下内容均来自武沛齐老师的课程笔记Fields的选择classMyForm(ModelForm):xx=form.CharField*("...")#新加不在数据库中的字段classMe......
  • 02.JavaScript学习笔记1
    JavaScript学习笔记1.强制类型转换当使用加号进行运算时,会将数字强制转换为字符串,然后再进行运算。constyear='1991';console.log(year+18);console.log(typeo......
  • js实现 delay 和 sleep函数
    console.log("====sleep===");//sleep等待几秒constsleep=(seconds)=>newPromise((resolve)=>setTimeout(resolve,seconds));asyncfunctionsleepTes......
  • 817笔记(轮播图js)
    网页轮播图步骤:鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮点击右侧按钮一次,图片往左播放一次,以此类推,左侧按钮同理图片播放的同时,下面小圆圈模块跟随一起变化......
  • 65注意力评分函数
    点击查看代码importmathimporttorchfromtorchimportnnfromd2limporttorchasd2l#掩蔽softmax操作#@savedefmasked_softmax(X,valid_lens):"""通......
  • Kubernetes学习笔记(十一):Node Selectors & Affinity
    NodeSelectorspod-definition.ymlspec:nodeSelector:size:Large##生效前需要先标记nodekubectllabelnodes<node-name><label-key>=<label-value>:......
  • 2022-08-17 第五组 罗佳明 学习笔记
    一、学习重点子查询(自连接)标量子查询:结果集只有一行一列(单行子查询)列子查询:结果集有一列多行行子查询:结果集一行多列表子查询:结果集多行多列日期格式二、学习内容......
  • 22/8/17深入理解计算机系统第七章笔记
    7.13库打桩机制库打桩:允许截获对共享库的调用,转而执行自己的代码。使用这个机制可以追踪某个特殊库函数的调用次数,验证和追踪它的输入和输出值,或者替换它。需要创建一个......