复健打卡第一天

news/2024/12/13 3:54:37/

拓展kmp指的是求的一个z数组: 假设这个字符串为a , 长度为len,在这个z数组中,z[i] 表示以 a[i] 开头的子串最多可以匹配前缀的长度。就好像下面这个字符串

a b b c d e a b b a c

0 0 0 0 0 0 3 2 1 1 0

这里的 2 表示以当前字符开头的字符串最对多可以匹配长度为2的前缀字符串

这个 3 表示以当前字符开始的字符串最多可以匹配长度为3的前缀字符串

那么问题来了他用来解决什么问题呢。

比如说给你一个长度为1e6 的字符串,你需要输出以每一个位置开头的字符串最多可以匹配多少长度的前缀字符串这时exkmp(恶心kmp 也就是扩展kmp) 也就派上用处了

对于单一串, 我们定义当前位置开头的字串可以匹配的前缀字符串长度为z[i];

扩展kmp是这样优化的。

我们首先定义一个区间【l,r】 我们暂且将他称为z-box(这个盒子的意义是,表示【l,r】区间的字符串是可以对应长度为(r - l + 1) 的前缀字符串。(很重要)

首先对于每一个i,他所匹配的区间为【i,i+z[i] - 1】; 也就是说 (i+z[i] - 1) 是当前点匹配的右端点我们在这里定义最右边的(i+ z[i] -1 )为维护区间的右端点r,当更新r的位置时,将更新r位置的i置为当前区间的左右端点置为l;

对于这个盒子来说,我们可以很明确的通过定义来找到这个【l,r] 这区间所对应的之前求出来的值,对! 那就是【1,( r- l + 1)]区间的字符串,也就是说,区间【l,r】的字符串是和区间【1,(r-l) +1]区间(也即是前缀)字符串是相同的!,是不是很神奇,这样我们就可以通过前面已经求出的区间来快速更新当前区间的z[i];

比如

  i  1 2 3 4 5 6 7 8 9a a a b a a a  b c z[i] 9 2 1 0 4||假设我们当前的4是求出来的那么对应的区间就是 【5,8】那么对应的匹配前缀字符串也就是【1,4】,此时假设我们将要求z[6], 我们可以直接借用a[6] 在 a[1~4] 中对应的位置,也就是 (z[6 - 5 + 1] )z[2] 的值来快速更新z[6]的值如果说我们 (6 + z[2] - 1) 的值小于当前的右区间r,我们就可以直接将z[6] = (z[6 - 5 + 1])=  z[2],如果大于的话就暴力向后直接匹配即可,由于每次r最多只会向后走len(字符串的长度) 所以这样匹配的时间复杂度是O(n)的。这也是扩展kmp的强大之处

对于算法流程。

  1. 如果说我们的i在 盒子内(也就是 i<=r) 则我们可以很容易的知道,他已经匹配上的区间为s[i,r] 匹配 s[i-l+1,r-l+1] 其实就是区间 【l,r] 和区间 【1,r-l + 1] 区间只取其【i,r】区间和z-box对应的区间
    此时需要讨论。

    (1). 如果 i + z[i-l+1] - 1 < r ,则表明当前匹配的前缀字符串长度加上i不会超过右区间,则此时z[i] = z[i-l+1];

    (2) 如果 i + z[i-l+1] - 1 >= r ,则表明当前匹配的前缀字符串长度加上i会超过右区间,此时我们先将z[i]更新为(r - i +1)再暴力向后匹配并且更新z[i]

  2. 如果i在盒外,我们直接暴力从i开始枚举即可。

  3. 此时我们已经求出来z[i] 了,此时我们需要判断如果I + z[i] - 1 > r 的话我们就直接更新区间使得(i = I , r = i + z[i] - 1 )即可

对于一个普通的板子就是接下来的代码:

void getextend(char a[], int n)
{z[1] = n ;for(int i = 2,l ,r = 0 ; i <=n ; i ++){//对应步骤1,如果在盒子内并且i+z[i-l+1] <  r的话答案就是z[i-l+1]//否者答案就是r-i+1的值,这个可以推导一下可以压缩。if(i <=  r)z[i] = min(z[i - l + 1],r - i + 1);while(a[1 + z[i]] == a[i + z[i]])z[i]++;    // 对应步骤2。if(i + z[i] - 1 > r)l = i ,r = i + z[i] - 1;//对应步骤3}
}

这是求一个串的z函数,那么就有同学问了(要是求一个字符串对于另一个字符串的的z函数怎么求) 这里其实我们只要理解了z-box的含义,就可以很容易的想到怎么改这个板子了

void get_z(char a[],int n , char b[], int m)
{//这里求字符串a匹配b前缀的z函数。for(int i = 2, l , r = 0; i <= n; i ++){if(i <= r)z[i] = min(z[i - l + 1],r - i+1);//这里我们只需要将z[i]保持在有效范围之内即可。while(i + z[i] <= n && 1 + z[i] <= m && b[1 + z[i]] == a[1 + z[i]])z[i] ++ ;if(i + z[i] - 1 > r)l = i , r = i + z[i] - 1;}
}

(可以参考下b站董晓老师讲的算法)


http://www.ppmy.cn/news/72877.html

相关文章

Linux_证书_Openssl实现对称加密、非对称加密、CA颁布证书

文章目录 OpenSSLopenssl实现对称加密openssl实现非对称加密生成密钥对非对称加密数字签名小结 根据CA颁布证书生成ca私钥和ca证书根据ca生成证书 尾声 OpenSSL 常用证书生成工具包括三个&#xff1a;ssh-keygen、cfssl、openssl。这里介绍 OpenSSL , OpenSSL 是一个开源项目&…

Java进程(基础)

基本概念 1、进程&#xff1a;程序的执行过程 2、线程&#xff1a;一个进程可以有单个线程也就是我们说的单线程&#xff0c;还可以有多个线程也就是我们说的多线程&#xff0c; 线程 1、当一个类继承了Thread类就可以当成一个线程用 2、我们会重写run方法写上我们自己的业务…

基于FPGA的多功能数字钟的设计

摘要 数字钟是采用数字电路实现对时、分、秒数字显示的计时装置,是人们日常 生活中不可少的必需品。本文介绍了应用FPGA芯片设计多功能数字钟的•种方 案,并讨讨论了有关使用FPGA芯片和VHDL语言实现数字钟设计的技术问题。 关键词数字钟、分频器、译码器、计数器、校时电路、…

BMPFont使用教程--免费的位图字体制作工具字体制作(2)

1、下载windows免费的位图字体制作工具Bitmap Font Generator 下载地址&#xff1a;BMFont - AngelCode.com 2、打开软件-> Edit -> Open Image Manager 3、点击Image -> Import Image,选择字符对应的图片&#xff0c;id就填写下面的48&#xff0c;代表0&#xff0c;…

栈的实现(附含经典例题)

&#x1f349;博客主页&#xff1a;阿博历练记 &#x1f4d6;文章专栏&#xff1a;数据结构与算法 &#x1f68d;代码仓库&#xff1a;阿博编程日记 &#x1f365;欢迎关注&#xff1a;欢迎友友们点赞收藏关注哦&#x1f339; 文章目录 &#x1f340;前言&#x1f3c4;‍♂️数…

基于PyQt5的图形化界面开发——Windows内存资源监视助手[附带编译exe教程]

基于PyQt5的图形化界面开发——Windows内存资源监视助手[附带编译exe教程] 0. 前言1. 资源信息获取函数——monitor.py2. UI界面——listen.py3. main.py4. 运行效果5. 编译 exe 程序6. 其他PyQt文章 0. 前言 利用 PyQt5 开发一个 windows 的资源监视助手&#xff0c;在使用虚…

为什么不胜任的人,反而获得晋升?

作者| Mr.K 编辑| Emma 来源| 技术领导力(ID&#xff1a;jishulingdaoli) 也许你有过这样的经历&#xff0c;自己勤勤恳恳地干活&#xff0c;每个月却只拿着微薄的薪水&#xff0c;有些人明明无法胜任工作&#xff0c;却像坐了火箭一样飞速晋升。这种现象在现实生活中无处不在…

简单快速的在openEuler22.03上部署openGauss2.10

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装openEuler1.创建虚拟机2.安装openEuler系统 二、安装openGauss1.设置openGauss2.创建数据库用户并设置权限3.设置防火墙4.远程连接openGauss 总结 前言…