KMP入门级别算法详解--终于解决了(next数组详解)

对于正常的字符串模式匹配,主串长度为m,子串为n,时间复杂度会到达O(m*n),而如果用KMP算法,复杂度将会减少线型时间O(m+n)。

设主串为ptr="ababaaababaa";,要比较的子串为a=“aab”;

KMP算法用到了next数组,然后利用next数组的值来提高匹配速度,我首先讲一下next数组怎么求,之后再讲匹配方式。

1、next数组详解

首先是理解KMP算法的第一个难关是next数组每个值的确定,这个问题困恼我很长时间,尤其是对照着代码一行一行分析,很容易把自己绕进去。

定义一串字符串

ptr = "ababaaababaa";

next[i](i从1开始算)代表着,除去第i个数,在一个字符串里面从第一个数到第(i-1)字符串前缀与后缀最长重复的长度。(这里看不懂继续往下看就行)

2、什么是前缀?

在“aba”中,前缀就是“ab”,除去最后一个字符的剩余字符串。

同理可以理解后缀。除去第一个字符的后面全部的字符串。

在“aba”中,前缀是“ab”,后缀是“ba”,那么两者最长的子串就是“a”;

在“ababa”中,前缀是“abab”,后缀是“baba”,二者最长重复子串是“aba”;

在“abcabcdabc”中,前缀是“abcabcdab”,后缀是“bcabcdabc”,二者最长重复的子串是“abc”;

这里有一点要注意,前缀必须要从头开始算,后缀要从最后一个数开始算,中间截一段相同字符串是不行的。

在next数组中,有两种定义方式:(定义方式可以先不用管,继续往下看,看懂了再回来看)

第一种是next[1]=-1,next[2]=-1。这里的-1代表没有匹配,1代表匹配了1位。

第二种是规定next[1]=0,next[2]=1。(第二种方法之后会讲)这里0与1是规定,后面的匹配中,如果1位匹配,则规定next值为2(就是匹配的位数+1)。

3、先看第一种规定方法:

再回到next[i]的定义,对于字符串ptr = "ababaaababaa";

next[1] = -1,字符串“a”,要进行next数组运算,也就是代表着除了第一个元素,它本身之前的 前缀 与 后缀 最长的重复子串,这里是空 ,即"",没有,我们记为-1,代表空。(0代表1位相同,1代表两位相同,依次累加)。

next[2] = -1,字符串为“ab”,要进行计算的字符串为“a”(要扣除当前字符“b”,所以只剩下“a”),没有前缀与后缀,故最长重复的子串是空,值为-1;

next[3] = -1,字符串为“aba”,要进行计算的字符串为“ab”(要扣除当前字符“a”,所以只剩下“ab”),前缀是“a”,后缀是“b”,最长重复的子串“”;

next[4] = 1,字符串为“abab”,要进行计算的字符串为"aba"(要扣除当前字符“b”,所以只剩下“aba”),前缀是“ab”,后缀是“ba”,最长重复的子串“a”;next数组里面就是最长重复子串字符串的长度

next[5] = 2,字符串为“ababa”,要进行计算的字符串为"abab"(要扣除当前字符“a”,所以只剩下“abab”),前缀是“aba”,后缀是“bab”,最长重复的子串“ab”;

next[6] = 3,字符串为“ababaa”,要进行计算的字符串为"ababa"(要扣除当前字符“a”,所以只剩下“ababa”),前缀是“abab”,后缀是“baba”,最长重复的子串“aba”;

next[7] = 1,字符串为“ababaaa”,要进行计算的字符串为"ababaa"(要扣除当前字符“a”,所以只剩下“ababaa”),前缀是“ababa”,后缀是“babaa”,最长重复的子串“a”;

next[8] = 1,字符串为“ababaaab”,要进行计算的字符串为"ababaaa"(要扣除当前字符“b”,所以只剩下“ababaaa”),前缀是“ababaa”,后缀是“babaaa”,最长重复的子串“a”;

next[9] = 2,字符串为“ababaaaba”,要进行计算的字符串为"ababaaab"(要扣除当前字符“a”,所以只剩下“ababaaab”),前缀是“ababaaa”,后缀是“babaaab”,最长重复的子串“ab”;

next[10] = 3,字符串为“ababaaabab”,要进行计算的字符串为"ababaaaba"(要扣除当前字符“b”,所以只剩下“ababaaaba”),前缀是“ababaaab”,后缀是“babaaaba”,最长重复的子串“aba”;

next[11] = 4,字符串为“ababaaababa”,要进行计算的字符串为"ababaaabab"(要扣除当前字符“a”,所以只剩下“ababaaabab”),前缀是“ababaaaba”,后缀是“babaaabab”,最长重复的子串“abab”;

next[12] = 5,字符串为“ababaaababaa”,要进行计算的字符串为"ababaaababa"(要扣除当前字符“a”,所以只剩下“ababaaababa”),前缀是“ababaaabab”,后缀是“babaaaababa”,最长重复的子串“ababa”;

所以字符串ptr = "ababaaababaa"的next数组为:-1,-1,-1,1,2,3,1,1,2,3,4,5

4、第二种方法中:

变化的只有下标,原理都一样。

这里我们定义next[1] = 0 , next[1] = 1;

再分析ptr字符串,ptr = "ababaaababaa";

跟上一个的情况类似,

next[1] = 0 ,事先定义好的

next[2] = 1 ,事先定义好的

next[3] = 1 ,最长重复的子串“”;1代表没有重复,2代表有一个字符重复。

next[4] = 2 ,最长重复的子串“a”;追偿的长度加1,即为2.

next[5] = 3 ,以下都跟之前的一样,这种方法是最长的长度再加上一就可以了。

next[6] = 4

next[7] = 2

next[8] = 2

next[9] = 3

next[10] = 4

next[11] = 5

next[12] = 6

以上是next数组的详细解释。next数组求值 是比较麻烦的,剩下的匹配方式就很简单了。

next数组用于子串身上,根据上面的原理,我们能够推出子串a=“aab”的next数组的值分别为0,1,2.(按照我说的第二种方式算的)。

首先开始计算主串与子串的字符,设置主串用i来表示,子串用j来表示,如果ptr[i]与a[i]相等,那么i与j就都加1:

prt[1]与a[1]相等,i++,j++:

用代码实现就是

if( j==0 ||  ptr[i]==a[j])
{
        ++i;
        ++j;
}

ptr[2]与a[2]不相等

此时ptr[2]!=a[2],那么令j = next[j],此时j=2,那么next[j] = next[2] = 1.那么此时j就等于1.这一段判断用代码解释的话就是:

if( ptr[i]!=a[j])
{
        j = next[j];
}
加上上面的代码进行组合:

在对两个数组进行比对时,各自的i,j取值代码:

while( i<ptr.length && j< a.length)
{
        if( j==0 || ptr[i]==a[i] )
        {
                ++i;
                ++j;
                next[i] = j;
        }
        else
         {
                j = next[j];
        }
}

此时将a[j]置于j此时所处的位置,即a[1]放到j=2处,因为在j=2时出现不匹配的情况。

此时再次计算是否匹配,可以看出来a[1]!=ptr[2],那么j = next[j],即此时j = next[1] = 0;

根据上面的代码,当j=0时,执行++i;++j;

此时就变为:

此时ptr[3] = a[1],继续向下走,下一个又不相等了,然后“aab”向后挪一位,这里不再赘述了,主要的思想已经讲明白了。到最后一直到i = 8,j=3时匹配成功,KMP算法结束。整个过程就结束了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/295478.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双非本科求职_面试经验

前言 继上次发实习面经已经过去一个多快两个月&#xff0c;这期间又面试了不少&#xff0c;但由于赶上秋招因此一直没写后续&#xff0c;今天做个回忆总结。由于菜鸟脑容量本来就不咋地&#xff0c;所以当初没写导致现在忘了好多&#xff0c;因此就写那些印象比较深的吧。交代…

【C++进阶02】多态

一、多态的概念及定义 1.1 多态的概念 多态简单来说就是多种形态 同一个行为&#xff0c;不同对象去完成时 会产生出不同的状态 多态分为静态多态和动态多态 静态多态指的是编译时 在程序编译期间确定了程序的行为 比如&#xff1a;函数重载 动态多态指的是运行时 在程序运行…

变限积分求导(带参,极限)

方法 一般形 带参数方程形 带极限型

IDEA 中 Tomcat 日志乱码

1、服务器输出乱码 修改 File -> settings -> Editor -> General ->Console 中&#xff0c;utf-8改为GBK&#xff0c;反之改成utf-8 2、Tomcat Localhost Log 或者 Tomcat Catalina Log乱码 进入Tomcat 中的conf文件中的logging.properties 哪个有问题改哪个&…

Ubuntu:VS Code上C++的环境配置

使用 VSCode 开发 C/C 程序 , 涉及到 工作区的.vscode文件夹下的3个配置文件&#xff08;均可以手动创建&#xff09; : ① tasks.json : 编译器构建 配置文件 ; ② launch.json : 调试器设置 配置文件 ; ③ c_cpp_properties.json : 编译器路径和智能代码提示 配置文件 ; …

竞赛保研 基于GRU的 电影评论情感分析 - python 深度学习 情感分类

文章目录 1 前言1.1 项目介绍 2 情感分类介绍3 数据集4 实现4.1 数据预处理4.2 构建网络4.3 训练模型4.4 模型评估4.5 模型预测 5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于GRU的 电影评论情感分析 该项目较为新颖&#xff0c;适合作为竞…

uniapp怎么动态渲染导航栏的title?

直接在接口请求里面写入以下&#xff1a; 自己要什么参数就写什么参数 本人仅供参考&#xff1a; this.name res.data.data[i].name; console.log(名字, res.data.data[i].name); uni.setNavigationBarTitle({title: this.name}) 效果&#xff1a;

界面控件DevExpress v23.2全新发布 - 官宣正式支持.NET 8

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具。屡获大奖的软件开发平台DevExpress 今年第一个重要版本v23.1正式发布&#xff0c;该版本拥有众多…

计算机网络(5):运输层

这一章应该是整个计算机网络对我们来说最重要的&#xff0c;也是用的最多的一部分。 运输层协议 进程之间的通信 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。…

c++ 使用 at()访问数组 抛出异常

1、说明 当我们定义一个数组vector b(10)后&#xff0c;b[]和b.at()都可以对v中元素进行访问&#xff0c;平时一般大家使用的都是v[]这种访问方法&#xff0c;以至于将v.at()这种访问方式忘记了。 2、vector[]和vector.at()的区别 b.v[]和b.at()都可以对v中元素进行访问&…

12.23C语言 指针

& 地址运算符&#xff0c;用于取地址 /*注释内容*/ //注释一行 *的意思&#xff1a;1.算术运算符 2.用于指针声明int *ptr;表示这个变量是一个指针3.数组元素访问&#xff1a;在数组名后面使用 * 可以表示数组的起始地址。例如&#xff1a; int arr[5] {1, 2, 3, 4, 5…

【5G PHY】NR参考信号功率和小区总传输功率的计算

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…