首页 > 其他分享 >KMP学习笔记

KMP学习笔记

时间:2024-11-08 17:20:35浏览次数:1  
标签:前缀 后缀 s1 笔记 学习 枚举 KMP pi

复习了一下KMP。与其说是复习,不如说是重学了一遍。

学习KMP实际上就是学习了前缀函数。下文大抵把OI-Wiki上关于前缀函数和KMP的部分内容说了一下。

前缀函数

定义

给定一字符串,对于它的每个前缀\(s[0,i-1]\),存在该子串的真前缀与真后缀相同,其中最大的一对前后缀的长度,记作:

\[\large \pi[i] \]

特别地,约定\(\pi[0]=0\)。
如字符串s="abcdabc",它的前缀函数如下:
\(\pi[1]=0,\pi[2]=0,\pi[3]=0,\pi[4]=0\)

\(\pi[5]=1\),因为子串abcda中,\(s[0,0]==s[4,4]\)

\(\pi[6]=2\),因为子串abcdab中,\(s[0,1]==s[4,5]\)

\(\pi[7]=3\),因为子串abcdabc中,\(s[0,2]==s[4,6]\)

因此\(s\)的前缀函数为:\([0,0,0,0,1,2,3]\)

注意 在某字符串\(s\)中,相等的前后缀形如abcabc不是abccba(虽然没有人会像我一样弄错)

求前缀函数

枚举

首先可以想到枚举。

求字符串\(s\)的每一个前缀\(s[0,i-1]\)的前缀函数值,从\(i-1\)开始枚举前后缀长度\(j\),一项一项判断它们是否相等。

复杂度\(O(n^3)\),显然在数据范围较大时会超时。

优化

注意到\(\pi[i]\)最多只会比\(\pi[i-1]\)多1,因此从\(\pi[i-1]+1\)开始枚举前后缀长度\(j\)。

复杂度\(O(n^2)\),虽然快了但是在数据范围较大时还是会超时。

继续优化

如果当\(j=\pi[i-1]+1\)时匹配成功那当然是最优的,现在的问题是如果匹配失败该怎么办。

注意到我们在上述枚举过程中浪费了很多时间去枚举不相等的前后缀的长度。

如图所示,当s=abcabdeabcabc(精心设计的字符串),\(i=13\)时,我们可以看到:

首先,令\(j=\pi[i-1]+1\),即\(j=6\),显然前缀abcabd不等于后缀 abcabc

然后,令\(j=5\),更显然前缀abcab不等于后缀bcabc

然后,令\(j=4\),更显然前缀abca不等于后缀cabc

然后,令\(j=3\),此时前缀abc等于后缀abc,匹配成功,\(\pi[13]=3\)。

我们可以看到,当\(j=5\)和\(4\)时,显然前后缀不可能相等。但是\(j=3\)时前后缀有可能相等,最终确实相等。

那么\(j=3\)有什么特点呢?我们可以注意到\(3=\pi[\pi[i-1]-1]+1\)。

也就是说,在读入新的字符时,如果第一次匹配失败,我们可以直接枚举第二长的前后缀,即令\(j=\pi[\pi[i-1]]+1\),其余情况,因为读入前这段前后缀就不相等,读入后,前后各加入一个字符,自然不可能相等。

于是我们就能得到一个状态转移方程:\(j[n]=\pi[j[n-1]-1]\)

实现

像这样:

std::vector<int> prefixFunction(std::string s)
{
    std::vector<int> pi(s.size());
    pi[0]=0;
    for(int i=1;i<=s.size();i++)
    {
        int j=pi[i-1];
        while(j>0&&s[i]!=s[j])
            j=pi[j-1];
       	if(s[i]==s[j])
            j++;
     	pi[i]=j;
    }
    return pi;
}

KMP

就是前缀函数的一种应用,用于求解形如“求字符串\(s1\)在\(s2\)中出现了几次,每次出现的位置”这类问题。

把\(s1\)接在\(s2\)前面,中间加一个字符集之外的字符(如:\(s1\)、\(s2\)均由小写字母组成的,可以加入一个大写字母,虽然笔者不建议这样做),然后求它的前缀函数。

P3375 【模板】KMP

当前缀函数值为\(len(s1)\)时,此时的位置\(i\)就是\(s2\)中出现一次\(s1\)的末位置。\(s2\)中这次出现\(s1\)的初位置,就是\(i-2len(s1)\)。


由于这篇文章说的是KMP,所以前缀函数其他应用就先不写了。

\[\huge End \]

标签:前缀,后缀,s1,笔记,学习,枚举,KMP,pi
From: https://www.cnblogs.com/neat-isaac/p/18533946/KMP

相关文章

  • 2025年入门深度学习或人工智能,该学PyTorch还是TensorFlow?
    随着2025应用人工智能和深度学习技术的举世泛气,还在迷茫于该选择哪个深度学习框架吗?PyTorch和TensorFlow是并立于深度学习世界两座巨塔,但是越来越多人发现,在2025年,PyTorch似乎比TensorFlow更为流行和被接受。下面我来分析一下这两个深度学习框架的发展历史,应用差异和现状,以......
  • SpringClud一站式学习之Springboot(一)
    SpringClud一站式学习之Springboot1.Springboot介绍2.Springboot中常用的包3.创建Springboot工程3.1.脚手架创建spingboot工程方法一:使用SpringInitializr方法二:使用SpringTool4forEclipse(STS4)方法三:使用IntelliJIDEA3.2.配置springboot3.3.编写应用接口......
  • 指针学习
    指针可以理解为保存地址的数据类型。其数据类型大小在32位系统中为4个字节,在64位系统中为8个字节。常量指针inta=10;constint*p=&a;const在int*之前的为常量指针,特点是*p不可变,但p可变。在图中红色框住的10是不允许通过指针修改的,但还是可以通过a这个途径修改,因为cons......
  • 前端技术对html的学习1
    html简介目录html简介HTML到底是什么?HTML版本HTML标签HTML文档结构HTML英文全称是HyperTextMarkupLanguage,中文译为“超文本标记语言”,专门用来设计和编辑网页。使用HTML编写的文件称为“HTML文档”,一般后缀为.html(也可以使用.htm,不过比较少见)。HTML文档是一种纯文......
  • 双连通分量学习笔记+杂题
    图论系列:前言:もしも明日がくるのならあなたと花を育てたいもしも明日がくるのならあなたと愛を語りたい走って笑って転んで相关题单:https://www.luogu.com.cn/training/641352一.割点与桥双连通分量是针对无向图来说的,有向图是强连通分量。在了解双连通分量之前需要先......
  • 2024最新网络安全学习路线(完整版)_网络安全2024 学习路径
    ......
  • 花8000元去培训机构学习网络安全值得吗,学成后就业前景如何?
    **......
  • TMC4671使用笔记
    1、单向DC电机开环测试voidTMC4671SinglePhaseDC_Test(){//电机类型和PWM配置//TMC4671_MOTOR_TYPE_N_POLE_PAIRS寄存器用于设置电机类型和极对数。//高16位(0x0001):电机类型。0:无电机1:单相直流电机2:两相步进电机3:三相无刷电机//低16位......
  • SqlServer 分页学习
    在B站上看到一个分页视频,老师讲的挺好,记录下来。想看原视频的可以去B站--1.建立Students表CREATETABLEstudents(IDINTPRIMARYKEYIDENTITY(1,2),NAMENVARCHAR(50)NOTNULL,SEXCHAR(6)CHECK(SEXIN('Male','Female')));GO--2.插入30条数据INSERTINTO......
  • 《程序员的修炼之道从小工到专家》阅读笔记2
    书中中间几个章节提到了编程相关的技术经验方法,有几点学习并找机会实践的。一是注重shell环境。,“所见即所得”同时可以理解为“所见即全部所得”,shell环境可以通过构建命令序列,让很多事情自动化,可以大大提高生产率。而我很少使用shell命令,所以不太熟悉它的好处,接下来的时间想认......