设有主串s和子串t,子串t的定位就是要在主串中找到一个与子串t相等的子串。通常把主串s称为目标串,把子串t称为模式串,因此定位也称为模式匹配

模式匹配成功是指在目标串s中找到一个模式串t;
不成功则指目标串s中不存在模式串t

Brute-Force算法

采用穷举的思路,从目标串s的第一个字符开始和模式串t的第一个字符开始比较

  • 若相等,则继续逐个比较后续字符
  • 不相等则从目标串s的第二个字符开始重新与模式串t的第一个字符进行比较

若匹配成功则返回主串中第一次出现模式串的位置,匹配失败返回-1

以目标串"abcdefgab",模式串"abcdex"为例

匹配次数 位置 结果
第1.1次匹配 i = 0,j = 0 a = a
1.2 i = 1,j = 1 b = b
1.3 i = 2,j = 2 c = c
1.4 i = 3,j = 3 d = d
1.5 i = 4,j = 4 e = e
1.6 i = 5,j = 5 f ≠ x
第2次匹配 i = 1,j = 0 b ≠ a
第3次匹配 i = 2,j = 0 c ≠ a
第4次匹配 i = 3,j = 0 d ≠ a
第5次匹配 i = 4,j = 0 e ≠ a
第6次匹配 i = 5,j = 0 f ≠ a
第7次匹配 i = 6,j = 0 g ≠ a
第8.1次匹配 i = 7,j = 0 a = a
8.2 i = 8,j = 1 b = b

当i = 9时,目标串下标越界,匹配结束。
结束时j没有指向模式串尾,说明没有匹配成功。返回-1

BF算法的实现

int
BF_index( char *s1, char *s2 )
{
int i = 0, j = 0;
int s1_len = strlen(s1);
int s2_len = strlen(s2);
while( i < s1_len && j < s2_len ){
if( s1[i] == s2[j] ){
i++;
j++;
}else{
i = i - j + 1;
j = 0;
}
}
if( j >= s2_len ){
return ( i - s1_len );
}else{
return -1;
}
}

最好时间复杂度为O( s2_len )
最坏时间复杂度为O( s1_len × s2_len )
平均时间复杂度为O( s1_len × s2_len )

这个算法简单易于理解,但效率不高,主要原因是:主串指针i在若干个字符序列比较相等后,若有一个字符比较不相等仍需回溯,重复做前面的事情。

KMP算法

引用了《大话数据结构》第5章KMP算法的内容

KMP算法专门针对BF算法中主串指针的回溯的问题进行优化,极大的提高了模式匹配算法的效率

例一

以目标串S"abcdefgab",模式串T"abcdex"为例

首先,模式串T有一个特点:首字符a仅在T[0]出现,后面的字符和T[0]均不相等。

第一次匹配的结果显示

S

[

i

]

=

T

[

i

]

(

i

=

0

,

1

,

2

,

3

,

4

)

S[i] = T[i]( i = 0,1, 2, 3, 4 )

S[i]=T[i](i=0,1,2,3,4)
由这个结果得出,在S串中的a也只在S[0]出现,这意味着S[1]、S[2]、S[3]、S[4]都不是a,这是可以直接得出的结论,所以在BF算法中的第2, 3, 4, 5次匹配都是多余的。

这里是KMP算法的关键。我们知道T串中首字符a与它后面的字符均不相等,然后通过判断得出T[1] = S[1],则可以省略BF算法的第2次匹配。同理,T[2]、T[3]、T[4]与S对应位置的字符判断相等后,也可以省略掉对应的匹配操作。

第6次匹配不能省略,因为在第1.6次匹配时只得出了S[5]≠T[5],没有得出S[5] ≠ T[0],所以这一步判断S[5]和T[0]需要保留,j变为0。

首字符仅在T[0]出现的情况比较特殊,下面讨论较一般的情况,即除了串首之外,首字符还分布在模式串不同的位置。

例二

以目标串abcababca,模式串abcabx为例

和例一相似,模式串T的特征是:首字符a出现的位置不唯一,分别是T[0]和T[3]

第一次匹配结果

S

[

i

]

=

T

[

i

]

(

i

=

0

,

1

,

2

,

3

,

4

)

S[i] = T[i]( i = 0,1, 2, 3, 4 )

S[i]=T[i](i=0,1,2,3,4)

  1. T[0] ≠ T[1],省略BF算法的第2次匹配
  2. T[0] ≠ T[2],省略BF算法的第3次匹配

T[0] = T[3],T[1] = T[4],而在第一次匹配我们已经知道T[3] = S[3],T[4] = S[4],即S[3] = T[0],S[4] = T[1]。而BF算法做多了两次匹配才得出相等的结论,显然第4次、第5次匹配是多余的。

在BF算法中,目标串指针i是不断地往回走的,在KMP算法中i不往后走,而是让模式串T的指针j变化。

j值的变化取决于T串中是否有重复的部分

把T串各个位置j值的变化定义为一个数组next,那么next的长度就是T串的长度。

模式串T中存在某个k( 0 < k < j ),使得以下等式成立

T

0

T

1

.

.

.

T

k

1

=

T

j

k

T

j

k

+

1

.

.

.

T

j

1

“T_0T_1...T_{k-1}”=“T_{j-k}T_{j-k+1}...T_{j-1}”

“T0​T1​...Tk−1​”=“Tj−k​Tj−k+1​...Tj−1​”
例如,T=ababc,当j=4时,T[ j ] = c,有T[0]T[2-1] = T[4-2]T[4-2+1],k = 2,所以next[4] = k
= 2

归纳起来就是

n

e

x

t

[

j

]

=

x

=

{

1

,

j

=

0

max

{

k

0

<

k

<

j

,

T

0

T

1

.

.

.

T

k

1

=

T

j

k

T

j

k

+

1

.

.

.

T

j

1

}

0

next[j] =x = \begin{cases} -1, 当j=0时\\ \max\{k | 0<k<j, 且“T_0T_1...T_{k-1}”=“T_{j-k}T_{j-k+1}...T_{j-1}”\}当此集合不为空时\\ 0其他情况 \end{cases}

next[j]=x=⎩⎪⎨⎪⎧​−1,当j=0时max{k∣0<k<j,且“T0​T1​...Tk−1​”=“Tj−k​Tj−k+1​...Tj−1​”}当此集合不为空时0其他情况​

推导next数组

T = abcdex

j 0 1 2 3 4 5
T[ j ] a b c d e x
next[ j ] -1 0 0 0 0 0
  1. j = 0,next[0] = -1
  2. j = 1,0 < k < 1,下标不能是小数,属于其他情况,next[1] = 0
  3. j = 2,k = 1,此时从0到k-1只有T[0],T[0] ≠ T[1],属于其他情况,next[2] = 0
  4. j = 3,k = 1或2,当k = 2时,T[0]T[1] ≠ T[1]T[2],属于其他情况,next[3] = 0

同理,最终得到T的next数组为-1,0,0,0,0,0

T = abcabx

j 0 1 2 3 4 5
T[ j ] a b c a b x
next[ j ] -1 0 0 0 1 2
  1. j = 0,next[0] = -1
  2. j = 1,同上说明,next[1] = 0
  3. j = 2,k = 1,同上,next[2] = 0
  4. j = 3,k = 1或2,同上,next[3] = 0
  5. j = 4,k = 1或2或3,当k = 1时,T[0] = T[3],next[4] = 1
    在后续的计算可以不用按照定义求解,从j-1出发,看能有多少个字符和T串开头匹配即可
  6. j = 5,有T[0]T[1] = T[3]T[4],得到 k = 2,next[j] = 2

最终next数组为-1,0,0,0,1,2

T = ababaaaba

  1. j = 0,next[0] = -1
  2. j = 1,同上,next[1] = 0
  3. j = 2,同上,next[2] = 0
  4. j = 3,有T[0] = T[2],next[3] = 1
  5. j = 4,有T[0]T[1] = T[2]T[3],next[4] = 2
  6. j = 5,有T[0]T[1]T[2] = T[2]T[3]T[4],next[5] = 3
  7. j = 6,有T[0] = T[5],next[6] = 1
  8. j = 7,有T[0] = T[6],next[7] = 1
  9. j = 8,有T[0]T[1] = T[6]T[7],next[8] = 2

next数组为-1,0,0,1,2,3,1,1,2

T = aaaaaaaab

  1. j = 0,next[0] = -1
  2. j = 1,next[1] = 0
  3. j = 2,T[0] = T[1],next[2] = 1
  4. j = 3,取k最大的情况即T[0]T[1] = T[1]T[2],next[3] = 2
  5. j = 8,next[8] = 7

next数组为-1,0,1,2,3,4,5,6,7

到这里就可以解释next[j] = k的含义了,这个等式表示模式串T[j]之前已经有k个字符成功匹配,下一次应该从T[k]开始匹配,而next[j] = -1表示T串当前没有加速匹配的信息,加1,下一次从0开始判断。

KMP算法的实现

void
KMP_getNext( char *s1, int *next )
{
int j = 0, k = -1;
next[0] = -1;
while( j < strlen(s1) - 1 ){
if( k == -1 || s1[j] == s1[k] ){
j++;
k++;
next[j] = k;
}else{
k = next[k];
}
}
} int
KMP_index( char *s1, char *s2 )
{
int i = 0, j = 0;
int next[MAXSIZE];
int s1_len = strlen(s1);
int s2_len = strlen(s2); KMP_getNext( s1, next );
while( i < s1_len && j < s2_len ){
if( j == -1 || s1[i] == s2[j] ){
i++;
j++;
}else{
j = next[j];
}
}
if( j >= s2_len ){
return ( i - s2_len );
}else{
return -1;
}
}

求next数组的时间复杂度为O(s2_len),KMP算法匹配时不回溯,则整体算法时间复杂度为O( s2_len + s1_len )

需要强调的是,KMP算法仅当T串和S串之间有许多部分匹配的情况下才体现出它的优势。

例题

已知字符串S为"abaabaabacacaabaabcc",模式串T为"abaabc",采用KMP算法进行匹配,第一次出现“失配”(s[i] != t[j])时,i = j = 5,则下次开始匹配时,i和j的值分别是

题目要求采用KMP算法,主串指针不回溯,i = 5,通过推导next数组找到next[j]

j 0 1 2 3 4 5
T[ j ] a b a a b c
next[ j ] -1 0 0 1 1 2

得到next[5] = 2
综上,i = 5, j = 2

改进的KMP算法

以S="aaaabcde",T="aaaaax"为例

求出next数组为-1、0、1、2、3、4。

当 i = 4,j = 4时,S[4] ≠ T[4],于是j = next[j],得到 j = 3。S[4] ≠ T[3],j = next[j],得到 j = 2…一直回退到 j = 0,才能i++j++,得到 i = 5,j = 0

用首位next[0]的值去代替与它相同的字符后续的next[j]的值可以省去上面回退的过程。

推导nextval数组

T = "ababaaaba"

先推导出next数组:-1,0,0,1,2,3,1,1,2

  1. j = 0,nextval[0] = -1
  2. j = 1,next[1] = 0,T[1] ≠ T[0],nextval[1] = next[1] = 0,保持不动
  3. j = 2,next[2] = 0,T[2] = T[0],所以nextval[2] = nextval[0] = -1
  4. j = 3,next[3] = 1,T[3] = T[1],所以nextval[3] = nextval[1] = 0
  5. j = 4,next[4] = 2,T[4] = T[2],所以nextval[4] = nextval[2] = -1
  6. j = 5,next[5] = 3,T[5] ≠ T[3],所以nextval[5] = next[5] = 3
  7. j = 6,next[6] = 1,T[6] ≠ T[1],所以nextval[5] = next[5] = 1
  8. j = 7,next[7] = 1,T[7] = T[1],所以nextval[7] = nextval[1] = 0
  9. j = 8,next[8] = 2,T[8] = T[2],所以nextval[8] = nextval[2] = -1

最终得到nextval数组为 -1, 0, -1, 0, -1, 3, 1, 0, -1

T = "aaaaax"

求出next数组为-1、0、1、2、3、4。

  1. j = 0,nextval[0] = -1
  2. j = 1,next[1] = 0,T[1] = T[0],nextval[1] = nextval[0] = -1
  3. j = 2,next[2] = 1,T[2] = T[1],nextval[2] = nextval[1] = -1
  4. j = 5,next[5] = 4,T[5] ≠ T[4],nextval[5] = next[5] = 4,保持不动

最终求出nextval数组为 -1,-1,-1,-1,-1,4

串的模式匹配 BF算法和KMP算法的更多相关文章

  1. 字符串匹配-BF算法和KMP算法

    声明:图片及内容基于https://www.bilibili.com/video/av95949609 BF算法 原理分析 Brute Force 暴力算法 用来在主串中查找模式串是否存以及出现位置 ...

  2. BF算法和KMP算法

    这两天复习数据结构(严蔚敏版),记录第四章串中的两个重要算法,BF算法和KMP算法,博主主要学习Java,所以分析采用Java语言,后面会补上C语言的实现过程. 1.Brute-Force算法(暴力法 ...

  3. 字符串匹配的BF算法和KMP算法学习

    引言:关于字符串 字符串(string):是由0或多个字符组成的有限序列.一般写作`s = "123456..."`.s这里是主串,其中的一部分就是子串. 其实,对于字符串大小关系 ...

  4. 串匹配模式中的BF算法和KMP算法

    考研的专业课以及找工作的笔试题,对于串匹配模式都会有一定的考察,写这篇博客的目的在于进行知识的回顾与复习,方便遇见类似的题目不会纠结太多. 传统的BF算法 传统算法讲的是串与串依次一对一的比较,举例设 ...

  5. 字符串匹配(BF算法和KMP算法及改进KMP算法)

    #include <stdio.h> #include <string.h> #include <stdlib.h> #include<cstring> ...

  6. BF算法和KMP算法 python实现

    BF算法 def Index(s1,s2,pos = 0): """ BF算法 """ i = pos j = 0 while(i < ...

  7. BF算法和KMP算法(javascript版本)

    var str="abcbababcbababcbababcabcbaba";//主串 var ts="bcabcbaba";//子串 function BF( ...

  8. 数据结构(十六)模式匹配算法--Brute Force算法和KMP算法

    一.模式匹配 串的查找定位操作(也称为串的模式匹配操作)指的是在当前串(主串)中寻找子串(模式串)的过程.若在主串中找到了一个和模式串相同的子串,则查找成功:若在主串中找不到与模式串相同的子串,则查找 ...

  9. 软件设计师_朴素模式匹配算法和KMP算法

    1.从主字符串中匹配模式字符串(暴力匹配) 2. KMP算法

随机推荐

  1. SSL基础知识及Nginx/Tomcat配置SSL

    HTTPS 是在 HTTPS 基础之上添加 SSL/TLS 使网络通讯加密,进而确保通信安全.可简记为 HTTPS = HTTP + SSL/TLS 本文档主要讲解常规SSL格式.Nginx 与 To ...

  2. javaScript知识储备

    javaScript知识储备 组成 ECMAScript(核心) 提供语法.变量等,遵循ECMA-262标准 DOM(文档对象模型) 提供操作HTML标签的API,遵循W3C规范 BOM(浏览器对象模 ...

  3. shell脚本之case语句

    case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行 ...

  4. Onenote实现OCR识别图片

    OCR识别推荐两个软件: 1.       Tesseract:一个开源的,由谷歌维护的OCR软件. 2.       Onenote:微软Office附带或者可以自己独立安装. 3.       O ...

  5. 20210826 Lighthouse,Miner,Lyk Love painting,Revive

    考场 T1 这不裸的容斥 T2 这不裸的欧拉路,先从奇数度点开始走,走不了就传送 T3 什么玩意,暴力都不会 T4 点分树??? 仔细想了一波,发现 T1 T2 都好做,T3 二分答案后可以暴力贪心, ...

  6. 20210717 noip18

    考前 从小饭桌出来正好遇到雨下到最大,有伞但还是湿透了 路上看到一个猛男搏击暴风雨 到了机房收拾了半天才开始考试 ys 他们小饭桌十分明智地在小饭桌看题,雨下小了才来 考场 状态很差. 开题,一点想法 ...

  7. vue.js框架图片上传组件

    html: <div id="app"> <div class="hello"> <div class="upload& ...

  8. 了解HTTP基本知识板块

    一.HTTP 协议概述 HTTP协议采用了请求/响座模型. 客户端向服务器发送-个请求,请求头包含请求的方法.URT..协议版本.以以 及包含请求修饰符.客户信息和内容的类似于MIME的消息结构. 服 ...

  9. Vue3.x全家桶+vite+TS-构建Vue3基本框架

    目录 一.搭建基础项目 1.vite创建项目 3.运行项目 2.环境变量设置介绍 vite配置多环境打包 二.配置Router 1.安装路由 2.配置路由 3.引入 三.配置Vuex 1.安装vuex ...

  10. FastAPI(4)- get 请求 - 路径参数 Path Parameters

    什么是路径 假设一个 url 是: http://127.0.0.1:8080/items/abcd 那么路径 path 就是 /items/abcd 路径参数 就是将路径上的某一部分变成参数,可通过 ...