[入门必看]数据结构4.2:串的模式匹配

这篇具有很好参考价值的文章主要介绍了[入门必看]数据结构4.2:串的模式匹配。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


第四章 串

小题考频:2
大题考频:0


4.2 串的模式匹配

难度:☆☆☆☆☆

知识总览

4.2.1_朴素模式匹配算法

4.2.2_1_KMP算法

[入门必看]数据结构4.2:串的模式匹配

4.2.2_2_求next数组

4.2.3_KMP算法的进一步优化


4.2.1_朴素模式匹配算法

什么是字符串的模式匹配

——在字符串内搜索某一段内容
[入门必看]数据结构4.2:串的模式匹配

查找功能

[入门必看]数据结构4.2:串的模式匹配

搜索引擎

[入门必看]数据结构4.2:串的模式匹配

  • 字符串模式匹配:在主串中找到与模式串相同的⼦串,并返回其所在位置。

有可能匹配不到模式串:[入门必看]数据结构4.2:串的模式匹配

子串——主串的一部分,一定存在
模式串——不一定在主串中找到


朴素模式匹配算法

——暴力解决问题
在主串中找出所有有可能与模式串相匹配的子串,并将这些子串和模式串一一进行对比
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配

[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
主串长度为n,模式串长度为m

朴素模式匹配算法:将主串中所有长度为m的子串依次与模式串对比,直到找到一个完全匹配的子串,或所有的子串都不匹配为止。

最多对比 n - m + 1 个子串

上一节中Index(S,T)函数的实现,采用的就是朴素模式匹配算法的思想。
[入门必看]数据结构4.2:串的模式匹配
1)int i=1 - 指明当前要匹配的子串是从哪个位置开始的;
2)(i<=n-m+1) - 表示最多对比n-m+1个子串;
3)SubString(sub,S,i,m); - 从主串S中,取出从位置i开始,长度为m的子串,放到sub里;
4)if(StrCompare(sub,T)!=0) ++i; - 子串和模式串对比,若不匹配,则匹配下一个子串
5)若能匹配,返回当前子串的起始位置i;
6)若都不能匹配,返回0

上述代码使用了:1)取子串的基本操作;2)对比两个字符串的基本操作

接下来:不使用字符串的基本操作,直接通过数组下标实现朴素模式匹配算法。


通过数组下标实现朴素模式匹配算法

设置两个扫描指针i和j,这两个指针指到哪就要把字符对比到哪。

Step1:
开始匹配第1个子串
对比主串和模式串的第1个字符
[入门必看]数据结构4.2:串的模式匹配
Step2:
如果指向的字符相等,那么让指针i和j分别后移
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
Step3:
到了第6个位置时,i和j所指向的字符不相等,则认为第一个子串匹配失败
[入门必看]数据结构4.2:串的模式匹配
若当前⼦串匹配失败,则主串指针 i 指向下⼀个⼦串的第⼀个位置,模式串指针 j 回到模式串的第⼀个位置

i = i - j + 2
(i - j:指针指向当前子串的前一个位置;+2:指向下一个子串的起始位置)
j = 1

Step4:
第1个子串匹配失败后:
i的值回到2
j的值回到1
然后开始匹配第2个子串
[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
匹配失败,则主串指针 i 指向下⼀个⼦串的第⼀个位置,模式串指针 j 回到模式串的第⼀个位置

Step4:
匹配下一个子串
[入门必看]数据结构4.2:串的模式匹配
失败,i 指向下一个子串的第一个位置,j 指向模式串第一个位置,开始匹配下一个子串。

Step5:
匹配成功!
[入门必看]数据结构4.2:串的模式匹配
若 j 指针大于模式串长度,j > T.length(模式串字符全部匹配成功),则当前⼦串匹配成功,返回当前⼦串第⼀个字符的位置 —— i - T.length

代码实现:

[入门必看]数据结构4.2:串的模式匹配
设主串⻓度为 n,模式串⻓度为 m,则
最坏时间复杂度 = O(nm)
[入门必看]数据结构4.2:串的模式匹配
最坏的情况,每个⼦串都要对⽐ m 个字符,共 n-m+1 个⼦串,复杂度 = O((n-m+1)m) = O ( n m ) O(nm) O(nm)

注:很多时候,n >> m,
保留数量级更大的项,把 O ( n m − m 2 + m ) O(nm-m^2+m) O(nmm2+m)简化为 O ( n m ) O(nm) O(nm)


4.2.2_1_KMP算法

——由D.E.Knuth,J.H.Morris和V.R.Pratt提出,因此称为KMP算法

对于朴素模式匹配算法,⼀旦发现当前这个⼦串中某个字符不匹配,就只能转⽽匹配下⼀个⼦串(从头开始)
[入门必看]数据结构4.2:串的模式匹配

因为我们并不知道主串里面这些字符到底是什么,所以我们必须从子串开头的第一个字符开始匹配。

如果匹配模式串时,在最后一个字符匹配失败,那么主串中之前这些字符就和模式串中的字符对应

那么在主串中匹配失败的位置,的之前的字符,是已知的,和模式串时保持一致的。
[入门必看]数据结构4.2:串的模式匹配
不匹配的字符之前,一定是和模式串一致的

朴素模式匹配算法中,匹配失败后只能从第2个子串开始重新匹配:
[入门必看]数据结构4.2:串的模式匹配
但是匹配第2个子串时,已知了主串中的前面这几个字符,发现刚开始就已经不匹配了,所以根本没有必要去检查和匹配。
[入门必看]数据结构4.2:串的模式匹配
第3个子串一样,已经知道了主串前面的几个字符,对不上,也没有必要去检查和匹配了。
[入门必看]数据结构4.2:串的模式匹配
匹配第4个子串时,主串里已知部分和模式串是能够匹配的,其他部分能否匹配现在还不知道,那么在这个子串中,可以从未知部分往后进行检查和匹配:
[入门必看]数据结构4.2:串的模式匹配


优化思路 - 模式串的最后一个位置不匹配

[入门必看]数据结构4.2:串的模式匹配
①不匹配的字符之前,一定是和模式串一致的;
②所以没有必要检查已知部分模式串不匹配的子串;
已知部分模式串相匹配的子串中,已经匹配的部分(已知部分)也不用再次对比;
④直接从【已知部分模式串相匹配的子串】的未知部分开始匹配。
[入门必看]数据结构4.2:串的模式匹配

跳过了中间几个子串的对比,也调过了当前子串已知的部分的对比,提高了算法效率

对于模式串 T = ‘abaabc’,当第6个元素匹配失败时,可令主串指针 i 不变(指向当前失配的字符),模式串指针 j=3(从模式串的第3个字符向后依次匹配)

得到的结论只和模式串有关,与匹配到主串的哪个位置没有关系。

验证(从第5个位置开始匹配):
[入门必看]数据结构4.2:串的模式匹配
匹配到当前子串的最后一个字符时,字符失配。
那么前面的字符就和模式串保持一致,即已知部分。
[入门必看]数据结构4.2:串的模式匹配
使用之前的结论:

对于模式串 T = ‘abaabc’,当第6个元素匹配失败时,可令主串指针 i 不变(指向当前失配的字符),模式串指针 j=3(从模式串的第3个字符向后依次匹配)

[入门必看]数据结构4.2:串的模式匹配
验证了该结论对模式串’abaabc’具有通⽤性,和主串没有半⽑钱关系。

以上是对于模式串T = ’abaabc’的第6个元素匹配失败的情况。


优化思路 - 其他位置不匹配

考虑其他位置的情况。

对于模式串 T = ‘abaabc’,当第5个元素匹配失败时? 怎么搞?

  • 第5个元素匹配失败,可以知道主串中前面4个元素的信息,与模式串保持一致

[入门必看]数据结构4.2:串的模式匹配

第2个子串也不能匹配上:
[入门必看]数据结构4.2:串的模式匹配

第3个子串也不能匹配上:
[入门必看]数据结构4.2:串的模式匹配

第4个子串可以匹配上:
[入门必看]数据结构4.2:串的模式匹配

此时可令主串指针i不变,模式串指针j = 2
从模式串的第二个元素开始匹配即可

  • 第4个元素匹配失败,可以知道主串中前面3个元素的信息,与模式串保持一致

[入门必看]数据结构4.2:串的模式匹配

第2个子串不能匹配上:
[入门必看]数据结构4.2:串的模式匹配

第3个子串可以匹配上:
[入门必看]数据结构4.2:串的模式匹配

此时可令主串指针i不变,模式串指针j = 2
从模式串的第二个元素开始匹配即可

  • 第3个元素匹配失败,可以知道主串中前面2个元素的信息,与模式串保持一致

[入门必看]数据结构4.2:串的模式匹配

第2个子串不能匹配上:
[入门必看]数据结构4.2:串的模式匹配

所以从第3个子串开始重新匹配:
[入门必看]数据结构4.2:串的模式匹配

此时可令主串指针i不变,模式串指针j = 1
从模式串的第一个元素开始匹配即可

  • 第2个元素匹配失败,可以知道主串中前面1个元素的信息,与模式串保持一致

[入门必看]数据结构4.2:串的模式匹配

所以从第2个子串开始重新匹配:
[入门必看]数据结构4.2:串的模式匹配

此时可令主串指针i不变,模式串指针j = 1
从模式串的第一个元素开始匹配即可

  • 第1个元素匹配失败,只能尝试匹配下一个子串:
    [入门必看]数据结构4.2:串的模式匹配

尝试匹配下一个子串:
[入门必看]数据结构4.2:串的模式匹配

结论:
对于模式串 T = ‘abaabc’
第6个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=3
第5个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第4个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第3个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第2个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第1个元素匹配失败时,匹配下⼀个相邻⼦串,令 j=0, i++, j++


上节例子对比

[入门必看]数据结构4.2:串的模式匹配
第六个字符匹配失败:
[入门必看]数据结构4.2:串的模式匹配
如果用朴素模式匹配算法:
[入门必看]数据结构4.2:串的模式匹配

朴素模式匹配算法,此时应令i = i - j + 3,j = 1;

如果用优化思路:
[入门必看]数据结构4.2:串的模式匹配

第6个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=3
第5个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第4个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第3个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第2个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第1个元素匹配失败时,匹配下⼀个相邻⼦串,令 j=0, i++, j++

[入门必看]数据结构4.2:串的模式匹配

优化之后,主串指针不需要回溯。
采用这种策略,效率大幅度提高。


对例子进行改造

第5个元素改成c:
[入门必看]数据结构4.2:串的模式匹配
第5个元素发生失配:
[入门必看]数据结构4.2:串的模式匹配
优化思路:
[入门必看]数据结构4.2:串的模式匹配

第6个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=3
第5个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第4个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第3个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第2个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第1个元素匹配失败时,匹配下⼀个相邻⼦串,令 j=0, i++, j++

此时第2个元素仍匹配失败:
[入门必看]数据结构4.2:串的模式匹配

第6个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=3
第5个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第4个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第3个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第2个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第1个元素匹配失败时,匹配下⼀个相邻⼦串,令 j=0, i++, j++

此时第1个元素仍匹配失败:
[入门必看]数据结构4.2:串的模式匹配

第6个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=3
第5个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第4个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第3个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第2个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第1个元素匹配失败时,匹配下⼀个相邻⼦串,令 j=0, i++, j++

[入门必看]数据结构4.2:串的模式匹配

此时第1个元素匹配,第2个元素匹配失败:
[入门必看]数据结构4.2:串的模式匹配

第6个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=3
第5个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第4个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第3个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第2个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第1个元素匹配失败时,匹配下⼀个相邻⼦串,令 j=0, i++, j++

[入门必看]数据结构4.2:串的模式匹配
此时第3个元素匹配失败:
[入门必看]数据结构4.2:串的模式匹配

第6个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=3
第5个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第4个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第3个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第2个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第1个元素匹配失败时,匹配下⼀个相邻⼦串,令 j=0, i++, j++

此时第1个元素匹配失败:
[入门必看]数据结构4.2:串的模式匹配

第6个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=3
当#pic_center第5个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第4个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=2
第3个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第2个元素匹配失败时,可令主串指针 i 不变,模式串指针 j=1
第1个元素匹配失败时,匹配下⼀个相邻⼦串,令 j=0, i++, j++

最终因为指针j超出模式串的范围而停止:
[入门必看]数据结构4.2:串的模式匹配
完成匹配工作。
整个过程中i不用回溯。

怎么⽤代码实现这个处理逻辑?
对于模式串T = ‘abaabc’
用数组来表示模式串指针需要修改的信息
[入门必看]数据结构4.2:串的模式匹配

特别地,第1个元素失配时,要将j = 0 再让 i++, j++

if (S[i] != T[j]) //模式串在第几个位置失配时,使用第几个位置的指针修改信息
	j = next[j];

if(j == 0) {i++; j++} //第1个位置时,匹配下一个相邻子串

KMP算法

是的,这就是KMP算法。
[入门必看]数据结构4.2:串的模式匹配
KMP算法的整体流程就是在进行模式匹配之前,需要先进行一个预处理

  1. 分析模式串,求出和模式串相对应的这个next数组
  2. 然后再利用next数组来进行模式匹配,整个匹配的过程主串的指针i是不需要回溯的。
利用next数组进行匹配

[入门必看]数据结构4.2:串的模式匹配

传入主串的值S、模式串的值T、和模式串相对应的这个next数组;
从主串的1和模式串的1位置开始往后匹配;
如果,主串的当前元素和模式串的当前元素相等的话,即匹配成功,i和j同时++;
并且当j == 0时,也需要让i和j同时++
否则,说明i和j所指元素不匹配,失配时让j = next[j]即可。


朴素模式匹配 v.s. KMP算法

[入门必看]数据结构4.2:串的模式匹配
对比发现,其实修改的部分即黄色框所框出部分,和需要传入一个next数组
有了next数组后,当主串和模式串发生失配时,不需要再修改主串的指针i让其回溯,只需要修改模式串的j指针即可。

朴素模式匹配算法,最坏的时间复杂度 O ( m n ) O(mn) O(mn)
KMP算法,最坏的时间复杂度 O ( m + n ) O(m+n) O(m+n)

其中,求 next 数组时间复杂度 O(m)
模式匹配过程最坏时间复杂度 O(n)

需要掌握手算next数组的方法。


4.2.2_2_求next数组

——(⼿算练习)
next数组的作⽤:当模式串的第 j 个字符失配时,从模式串的第 next[j] 继续往后匹配

练习1:

手算next数组

[入门必看]数据结构4.2:串的模式匹配的next数组:
[入门必看]数据结构4.2:串的模式匹配
next数组的下标与字符串的下标一一对应,1~6。

  • next[1]:
    【当模式串的第一个字符匹配失败时,模式串指针j应该指向什么位置】

[入门必看]数据结构4.2:串的模式匹配
当第一个字符匹配失败时,直接让i++,j++,即开始匹配后一个子串和模式串:
[入门必看]数据结构4.2:串的模式匹配

该逻辑对于任何一个模式串都是一样的,只要第1个字符发生了不匹配的情况,只能让他匹配下一个子串。
所以所有的模式串next[1]肯定都是0
[入门必看]数据结构4.2:串的模式匹配

  • next[2]:
    【当模式串的第二个字符匹配失败时,模式串指针j应该指向什么位置】
    [入门必看]数据结构4.2:串的模式匹配
    此时,只能让模式串往后滑动一位,即指向1位置:
    [入门必看]数据结构4.2:串的模式匹配

该逻辑对于任何一个模式串都是一样的,只要第2个字符发生了不匹配的情况,应尝试匹配模式串的第1个字符。
所以所有的模式串next[2]肯定都是1
[入门必看]数据结构4.2:串的模式匹配

  • next[3]:
    【当模式串的第三个字符匹配失败时,模式串指针j应该指向什么位置】

[入门必看]数据结构4.2:串的模式匹配

在不匹配的位置前划出分界线,左边的部分是已知的,右边是未知的。
尝试让模式串一步一步往右移,过程中,观察分界线左边部分能否匹配上。
直到分界线之前“能对上”,或模式串完全跨过分界线为止
此时j指向哪儿,next数组值就是多少。

往右移动一步:[入门必看]数据结构4.2:串的模式匹配
发现分界线左边的g和o失配,说明模式串右移一步不够。
继续往右移动一步:
[入门必看]数据结构4.2:串的模式匹配

此时整个模式串跨过了分界线,此时要继续向后检查模式串的j和右边位置未知元素i是否匹配。
此时j的值为1,那么next[3] = 1
[入门必看]数据结构4.2:串的模式匹配

  • next[4]:
    【当模式串的第四个字符匹配失败时,模式串指针j应该指向什么位置】

[入门必看]数据结构4.2:串的模式匹配
Step1:分界线
[入门必看]数据结构4.2:串的模式匹配

分界线左边的值是可以确定的,逐步向右移动模式串看是否匹配,或者模式串跨过分界线。

Step2:右移一步
[入门必看]数据结构4.2:串的模式匹配
失配。

Step3:右移两步
[入门必看]数据结构4.2:串的模式匹配
失配。

Step4:右移三步
[入门必看]数据结构4.2:串的模式匹配

此时模式串跨过分界线,j指向1,next[4] = 1
[入门必看]数据结构4.2:串的模式匹配

  • next[5]:
    【当模式串的第五个字符匹配失败时,模式串指针j应该指向什么位置】

[入门必看]数据结构4.2:串的模式匹配
重复以上步骤,逐步向右移动模式串,找匹配部分。

最终找到了分界线左边的匹配部分,接下来检查右边i和j是否匹配:
[入门必看]数据结构4.2:串的模式匹配

此时j指向2,next[5] = 2
[入门必看]数据结构4.2:串的模式匹配

  • next[6]:
    【当模式串的第六个字符匹配失败时,模式串指针j应该指向什么位置】

[入门必看]数据结构4.2:串的模式匹配
重复以上步骤,逐步向右移动模式串,找匹配部分。

最终模式串跨过分界线,之后需要检查i和模式串第1个字符能否匹配:
[入门必看]数据结构4.2:串的模式匹配

此时j指向1,next[6] = 1
[入门必看]数据结构4.2:串的模式匹配

使⽤next数组进⾏模式匹配

给出主串S为googlo goo google,在其中找到模式串google:
[入门必看]数据结构4.2:串的模式匹配
已经手算得到next数组:
[入门必看]数据结构4.2:串的模式匹配

开始进行模式匹配
Step1:第6个元素匹配失败
[入门必看]数据结构4.2:串的模式匹配

j = 6 时失配,此时让 j = next[j],即 j = next[6],
接下来让 j = 1

[入门必看]数据结构4.2:串的模式匹配
Step2:第1个元素匹配失败

j = 1 时失配,此时让 j = next[j],即 j = next[1],
接下来让 j = 0

[入门必看]数据结构4.2:串的模式匹配
j = 0时让i和j都++
[入门必看]数据结构4.2:串的模式匹配
Step3:第5个元素匹配失败
[入门必看]数据结构4.2:串的模式匹配

j = 5 时失配,此时让 j = next[j],即 j = next[5],
接下来让 j = 2

[入门必看]数据结构4.2:串的模式匹配
Step4:匹配成功
[入门必看]数据结构4.2:串的模式匹配

练习2:

求模式串T = ababaa的next数组
模式串长度是6,next组数有next[1]~next[6][入门必看]数据结构4.2:串的模式匹配
总结规则:
[入门必看]数据结构4.2:串的模式匹配
Step1:next[1]=0和next[2]=1
[入门必看]数据结构4.2:串的模式匹配
Step2:求next[3]
[入门必看]数据结构4.2:串的模式匹配

模式串跨过分界线,此时j = 1,next[3] = 1
[入门必看]数据结构4.2:串的模式匹配

Step3:求next[4]
[入门必看]数据结构4.2:串的模式匹配

分界线左边匹配成功,此时j = 2,next[4] = 2
[入门必看]数据结构4.2:串的模式匹配

Step4:求next[5]
[入门必看]数据结构4.2:串的模式匹配

分界线左边匹配成功,此时j = 3,next[5] = 3
[入门必看]数据结构4.2:串的模式匹配

Step5:求next[6]
[入门必看]数据结构4.2:串的模式匹配

分界线左边匹配成功,此时j = 4,next[6] = 4
[入门必看]数据结构4.2:串的模式匹配

练习3:

[入门必看]数据结构4.2:串的模式匹配
[入门必看]数据结构4.2:串的模式匹配
求next数组:
[入门必看]数据结构4.2:串的模式匹配


4.2.3_KMP算法的进一步优化

——求nextval数组

KMP算法优化的思路:
以之前小节中的模式串abaabc为例,已经求出了这个模式串的next数组。
[入门必看]数据结构4.2:串的模式匹配
模式串abaabc的next数组:
[入门必看]数据结构4.2:串的模式匹配


例1:第三个位置失配

如果第三个位置发生失配时,让j指针指回next[3]
[入门必看]数据结构4.2:串的模式匹配
此时说明主串中第三个字符和模式串的第三个字符a是肯定不相等的。
主串中第三个字符肯定不是a
[入门必看]数据结构4.2:串的模式匹配
如果此时按照next数组中[入门必看]数据结构4.2:串的模式匹配next[3] = 1,来进行匹配,那么这一次匹配也一定是失败的。

因为模式串中的第一个字符为a,而且已经知道了主串中的第三个字符不为a
这次匹配失败后,那么应该让j指针等于next[j],也就是等于0。

所以当第3个字符匹配失败的时候,让j = 0,即next[3] = 0。

优化后:
[入门必看]数据结构4.2:串的模式匹配
第3个位置失配时:
[入门必看]数据结构4.2:串的模式匹配

直接让j = next[3] = 0
[入门必看]数据结构4.2:串的模式匹配

那么下一个匹配的位置就会直接跳过这个字符,因为会让i和j同时++
[入门必看]数据结构4.2:串的模式匹配


例2:第五个位置失配

假设模式串在第5个位置失配,那么KMP算法会让j = next[5] = 2。
[入门必看]数据结构4.2:串的模式匹配

虽然暂时不知道主串i指针所指位置的字符是什么,但是肯定不是b。
[入门必看]数据结构4.2:串的模式匹配
所以如果按照刚才让j指针指向2位置,接下来的这次匹配一定是失败的,因为字符2和字符5都是b,然后还需要让j = next[2] = 1。

所以干脆就一步到位,让next[5] =next[2]的值:
即j = next[5] = 1:
[入门必看]数据结构4.2:串的模式匹配

此时回到上面的情况,如果字符5发生失配,j = next[5],直接就j = 1,节约了一个步骤,没有必要再让next[5] = 2,即比较第二个字符,因为肯定匹配不上。

这就是优化。

当然不是所有next数组都可以优化,优化思路为:
需要判断next数组所指的字符和原本失配的字符是否相等

  • 如果这两个字符不相等,那么next数组保持不变;
  • 如果这两个字符相等,那么next数组就可以进行优化。

[入门必看]数据结构4.2:串的模式匹配
将next数组优化成nextval,然后再KMP算法匹配的时候,用nextval数组替代next数组,其他一样。


练习1:

对于这个模式串:
[入门必看]数据结构4.2:串的模式匹配
求出其next数组:

[入门必看]数据结构4.2:串的模式匹配
然后手算其nextval数组:
[入门必看]数据结构4.2:串的模式匹配
首先,nextval[1]的值直接写=0;
然后,如果当前的next[j]所指字符,和目前j所指的字符不相等,就让nextval的值等于next的值,所以nextval[2]应该等于1。

  • 如,next[2] = 1,所指字符为第1个,即a,和目前j所指的第2个字符b不相等,那么nextval[1] = next[1] = 1
    [入门必看]数据结构4.2:串的模式匹配
  • 如,next[3] = 1,所指字符为第1个,即a,但是目前j所指第3个字符为a相等,那么就让nextval[3] = nextval[next[3]] = nextval[1] = 0,即跳到1对比失败的那个next,即next[1] = 0。

也就是说直接把next[3]的值优化为next[1]的值,即0。
[入门必看]数据结构4.2:串的模式匹配
同样的,第四个字符b失配时,next[4] = 2,跳到第二个字符b时相等,那么
nextval[4] = nextval[next[4]] = nextval[2] = 1
[入门必看]数据结构4.2:串的模式匹配
第五个字符a失配时,next[5] = 3,跳到第三个字符a时相等,那么
nextval[5] = nextval[next[5]] = nexvalt[3] = 0

第六个字符a失配时,next[6] = 4,跳到第四个字符b时不相等,那么
nextval[6] = next[6] = 4
[入门必看]数据结构4.2:串的模式匹配
最终求出了nextval数组:
[入门必看]数据结构4.2:串的模式匹配


练习2:

对于模式串:
[入门必看]数据结构4.2:串的模式匹配
其next数组是:
[入门必看]数据结构4.2:串的模式匹配
nextval[1] = 0;
第二个字符和next[2]所指字符相等,nextval[2] = nextval[next[2]] = 0;
第三个字符和next[3]所指字符相等,nextval[3] = nextval[next[3]] = 0;
第四个字符和next[4]所指字符相等,nextval[4] = nextval[next[4]] = 0;
第五个字符和next[5]所指字符不相等,nextval[5] = [next[5] = 4。

所以求得其nextval数组为:

[入门必看]数据结构4.2:串的模式匹配


优化KMP算法

——感受一下优化的力量

优化前:

[入门必看]数据结构4.2:串的模式匹配
此时匹配到第4个字符,失配。到next[4] = 3:
[入门必看]数据结构4.2:串的模式匹配
此时第3个字符依然失配。到next[3] = 2:
[入门必看]数据结构4.2:串的模式匹配
此时第2个字符依然失配。到next[2] = 1:
[入门必看]数据结构4.2:串的模式匹配
此时第1个字符依然失配。到next[1] = 0:
[入门必看]数据结构4.2:串的模式匹配
j = 0时,i和j同时++:
[入门必看]数据结构4.2:串的模式匹配
此时匹配成功:
[入门必看]数据结构4.2:串的模式匹配

优化后:

[入门必看]数据结构4.2:串的模式匹配
此时匹配到第4个字符,失配。到nextval[4] = 0:
[入门必看]数据结构4.2:串的模式匹配
j = 0时,i和j同时++:
[入门必看]数据结构4.2:串的模式匹配

此时就跳过了刚才中间部分的对比。

接下来匹配成功:
[入门必看]数据结构4.2:串的模式匹配

把next数组优化为nextval数组之后,中间减少了很多没有必要的对比。


知识回顾与重要考点

4.2.1_朴素模式匹配算法

[入门必看]数据结构4.2:串的模式匹配

  • 暴力解法:把所有有可能的子串遍历一遍
  • 最坏时间复杂度 = O ( n m ) O(nm) O(nm)
    ——每一个子串前面所有元素都和模式串匹配,只有最后一个元素和模式串不匹配

4.2.2_1_KMP算法

  • 需要掌握手算next数组的方法
  • 记住KMP算法的整体时间复杂度 O ( m + n ) O(m+n) O(m+n)
    ——预处理(next数组)时间复杂度 O ( m ) O(m) O(m);模式匹配时间复杂度 O ( n ) O(n) O(n)

4.2.2_2_求next数组

[入门必看]数据结构4.2:串的模式匹配


4.2.3_KMP算法的进一步优化

[入门必看]数据结构4.2:串的模式匹配文章来源地址https://www.toymoban.com/news/detail-422466.html

  • 串在考试中的地位

到了这里,关于[入门必看]数据结构4.2:串的模式匹配的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [入门必看]数据结构6.1:图的基本概念

    小题考频:33 大题考频:11 难度:☆☆☆☆ 6.1.1 图的基本概念 图的定义 A、B、C……这些元素的集合就是顶点集V,图G中顶点个数也成为图G的阶; 连接各个顶点的边的集合就是边集E 注意。图里面的一条边,连接的u,v两个顶点必须是刚才给出的顶点集中的顶点,边的两头必须

    2024年02月06日
    浏览(44)
  • 【数据结构】朴素模式匹配 & KMP算法

    🌈 自在飞花轻似梦,无边丝雨细如愁 🌈   🌟 正式开始学习数据结构啦~此专栏作为学习过程中的记录 🌟 子串的定位操作通常称为串的模式匹配,它求的是模式串在主串中的位置,而朴素模式匹配就是一种不断移动主串指针,每一次都和模式串依次进行比较的暴力求解方法

    2024年02月16日
    浏览(45)
  • 【数据结构】数组和字符串(十四):字符串匹配1:朴素的模式匹配算法(StringMatching)

      字符串(String)是由零个或多个字符(char)顺序排列组成的有限序列,简称为串。例如 “good morning”就是由12个字符构成的一个字符串。一般把字符串记作: S = ′ ′ a 0 a 1 … a n − 1 ′ ′ S=\\\'\\\'a_{0} a_{1}…a_{n-1}\\\'\\\' S = ′′ a 0 ​ a 1 ​ … a n − 1 ′′ ​   其中S是串名,引号中

    2024年02月05日
    浏览(74)
  • 数据结构课设:基于字符串模式匹配算法的病毒感染检测问题

    1.掌握字符串的顺序存储表示方法。 2.掌握字符串模式匹配算法BF算法或KMP算法的实现。 问题描述 医学研究者最近发现了某些新病毒,通过对这些病毒的分析,得知它们的DNA序列都是环状的。现在研究者已收集了大量的病毒DNA和人的DNA数据,想快速检测出这些人是否感染了

    2023年04月27日
    浏览(50)
  • 【数据结构】模式匹配之KMP算法与Bug日志—C/C++实现

    ​ 🌈 个人主页: Sarapines Programmer 🔥  系列专栏: 《数据结构奇遇记》 🔖墨香寄清辞: 墨痕寄壮志,星辰梦未满。 通幽径心凝意,剑指苍穹势如山。 目录 🌞1. 模式匹配的基本概念 🌞2. 模式匹配的解决办法 🎈2.1 暴力匹配(BF)算法 🎈2.2 KMP算法 🤖2.3 BUG记录_KMP算法 1

    2024年02月04日
    浏览(54)
  • 数据结构--串的基本操作

    第五话 数据结构之串 文章目录 一、了解什么是串 二、串的基本特征 三、串的基本操作 串的初始化 串的输出  四、串的匹配模式 五、总结 串(即字符串)是一种特殊的线性表,在信息检索、文本编辑等领域有广泛的应用。其特殊性体现在组成线性表的每个数据元素是单个

    2023年04月17日
    浏览(56)
  • 【数据结构】串的基本定义及操作

    🌈积薪高于山,焉用先后别 🌈   🌟 正式开始学习数据结构啦~此专栏作为学习过程中的记录 🌟 概念熟记: 串 是由 0个或多个字符 组成的有限的序列,记作 S = ′ a 1 a 2 . . . a n ′ S=\\\'a_1a_2...a_n\\\' S = ′ a 1 ​ a 2 ​ ... a n ′ ​ ,其中,当 n = 0 n=0 n = 0 时表示空串 串 中任意多个

    2024年02月06日
    浏览(56)
  • 【数据结构】串的基本操作及应用

    分别定义两个结构体——串的定长顺序存储、串的堆式顺序存储   问题: 1、编写函数,串用定长顺序存储表示来实现串的基本操作; 2、 编写串的匹配算法,实现查找功能。 算法思想阐述: BF 算法:首先S[1] 和T[1] 比较,若相等,则再比较S[2] 和T[2] ,一直到T[M] 为止;若

    2023年04月26日
    浏览(44)
  • 数据结构—串的详细解释(含KMP算法)

    1.1串的定义 串:串是由零个或多个字符组成的有限序列,又叫字符串(其的存储结构包含顺序表存储、单链表存储的形式。) 一般记为s=\\\"a1a2a3....an\\\"(n=0),其中,s是串的名称,用双引号(也可以使用单引号)括起来的字符序列是串的值,注意引号不是串的内容。ai(i=i=n)可以是字母、

    2023年04月09日
    浏览(45)
  • C/C++数据结构:串的五个常用操作

    2024年02月07日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包