24、蛤蟆的数据结构笔记之二十四串的模式匹配算法

24、蛤蟆的数据结构笔记之二十四串的模式匹配算法

本篇名言:“燧石受到的敲打越厉害,发出的光就越灿烂。 --
马克思

来看下两个算法,BF和KMP算法在串的模式匹配中实现。

欢迎转载,转载请标明出处:

1.  BF算法

BF(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

首先S[1]和T[1]比较,若相等,则再比较S[2]和T[2],一直到T[M]为止;若S[1]和T[1]不等,则T向右移动一个字符的位置,再依次进行比较。如果存在k,1≤k≤N,且S[k+1…k+M]=T[1…M],则匹配成功;否则失败。

该算法最坏情况下要进行M*(N-M+1)次比较,时间复杂度为O(M*N)。

如下图1 :

2.  KMP算法

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。

主串:a b a c aa b a c a b a c a b a a b b,下文中我们称作T

模式串:a b a c ab,下文中我们称作W

在暴力字符串匹配过程中,我们会从T[0] 跟 W[0] 匹配,如果相等则匹配下一个字符,直到出现不相等的情况,此时我们会简单的丢弃前面的匹配信息,然后从T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配的情况。这种简单的丢弃前面的匹配信息,造成了极大的浪费和低下的匹配效率。

然而,在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。

比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配。右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠。

在第一次匹配过程中

T: a b a c a a b a c a b a c a b a a b b

M: a b a c ab

在T[5]与W[5]出现了不匹配,而T[0]~T[4]是匹配的,现在T[0]~T[4]就是上文中说的已经匹配的模式串子串,现在移动找出最长的相同的前缀和后缀并使他们重叠:

T: a b a c aab a c a b a c a b a a b b

M: a b a c ab

然后在从上次匹配失败的地方进行匹配,这样就减少了匹配次数,增加了效率。

如下图2

3.  算法实现

l  BF实现

BF实现,通过第一个字母开始,一个字母一个字母的回溯实现。

最后返回第几个字母开始匹配成功。

int BFMatch(char *s,char
*p)

{

inti,j;

i=0;

while(i< strlen(s))

{

j=0;

while(s[i]==p[j]&&j< strlen(p))

{

i++;

j++;

}

if(j==strlen(p))

returni-strlen(p);

i=i-j+1;               
//指针i回溯

}

return-1;

}

l  KMP实现

KMP算法多了一个getNext函数,该函数是将模式匹配项进行处理得到一个数组。

数组中是一组整型数字,个数和匹配字符串一样多。每个整型表示,如果NEXT值为{-1,0,0,0,1,2,3,4,5},如果,模式匹配和字符串在第7个字母匹配出错(第7个数组值是3),那么模式匹配串重新往右移动3个字母然后重新和字符串进行匹配,如果又匹配失败,那么第3个数组是0,那么匹配字符串重新和字符串进行到的下一个字母进行匹配。

//getNetx

void getNext(char *p,int
*next)

{

intj,k;

next[0]=-1;

j=0;

k=-1;

while(j< strlen(p)-1)

{

if(k==-1||p[j]==p[k])   
//匹配的情况下,p[j]==p[k]

{

j++;

k++;

next[j]=k;

}

else

{                 
//p[j]!=p[k]

k=next[k];

}

}

}

//KMP

int KMPMatch(char *s,char
*p)

{

intnext[100];

inti,j;

i=0;

j=0;

getNext(p,next);

while(i< strlen(s))

{

if(j==-1||s[i]==p[j])

{

i++;

j++;

}

else

{

j=next[j];      
//消除了指针i的回溯

}

if(j==strlen(p))

{

returni-strlen(p);

}

}

return-1;

}

4.  BF和KMP算法源码

最后如下图3所示:

#define
_CRT_SECURE_NO_WARNINGS

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#define
MAX_SIZE255   
//定义字符串的最大长度

typedef
unsigned
char SString[MAX_SIZE];//数组第一个保存长度

//BF

int BFMatch(char *s,char
*p)

{

inti,j;

i=0;

while(i< strlen(s))

{

j=0;

while(s[i]==p[j]&&j< strlen(p))

{

i++;

j++;

}

if(j==strlen(p))

returni-strlen(p);

i=i-j+1;               
//指针i回溯

}

return-1;

}

//getNetx

void getNext(char *p,int
*next)

{

intj,k;

next[0]=-1;

j=0;

k=-1;

while(j< strlen(p)-1)

{

if(k==-1||p[j]==p[k])   
//匹配的情况下,p[j]==p[k]

{

j++;

k++;

next[j]=k;

}

else

{                 
//p[j]!=p[k]

k=next[k];

}

}

}

//KMP

int KMPMatch(char *s,char
*p)

{

intnext[100];

inti,j;

i=0;

j=0;

getNext(p,next);

while(i< strlen(s))

{

if(j==-1||s[i]==p[j])

{

i++;

j++;

}

else

{

j=next[j];      
//消除了指针i的回溯

}

if(j==strlen(p))

{

returni-strlen(p);

}

}

return-1;

}

int main()

{

inta, b;

chars[MAX_SIZE], p[MAX_SIZE];

printf("请输入模式串:");

scanf("%s",&s);

printf("请输入子串:");

scanf("%s",&p);

a =BFMatch(s, p);

b =KMPMatch(s, p);

if(a!= -1)

{

printf("使用BF算法:%d\n",
a);

}

else

{

printf("未匹配\n");

}

if(b!= -1)

{

printf("使用KMP算法:%d\n",
a);

}

else

{

printf("未匹配\n");

}

system("pause");

}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-17 03:55:13

24、蛤蟆的数据结构笔记之二十四串的模式匹配算法的相关文章

26、蛤蟆的数据结构笔记之二十六串应用之建立词索引表

26.蛤蟆的数据结构笔记之二十六串应用之建立词索引表 本篇名言:"生命是一条美丽而曲折的幽径,路旁有妍花的丽蝶,累累的美果,但我们很少去停留观赏,或咀嚼它,只一心一意地渴望赶到我们幻想中更加美丽的豁然开朗的大道.然而在前进的程途中,却逐渐树影凄凉,花蝶匿迹,果实无存,最后终于发觉到达一个荒 漠.-- 萨拉" 1.  信息检索 信息检索是计算机应用的重要领域之一.为了提高图书馆数目检索的效率,建立书名关键词索引,可以实现读者快速检索书目的自动化,即读者根据关键词索引表,读者可以方便查询到

29、蛤蟆的数据结构笔记之二十九数组之硬币抛掷模拟

29.蛤蟆的数据结构笔记之二十九数组之硬币抛掷模拟 本篇名言:"人生是各种不同的变故.循环不已的痛苦和欢乐组成的.那种永远不变的蓝天只存在于心灵中间,向现实的人生去要求未免是奢望.-- 巴尔扎克" 欢迎转载,转载请标明出处: 1.  硬币抛掷 如果抛掷硬币N次,看到头像的期望值是N/2次,但实际值也可能是0~N次,在程序中进行M次试验,M和N都在代码中定义.它使用一个数组f来跟踪出现"i次头像"的概率,其中0≤j≤N.然后打印试验结果的柱状图,每出现10次用1个星号

22、数据结构笔记之二十二串相关概念

22.数据结构笔记之二十二串相关概念 本篇名言:"现实是此岸,理想是彼岸,中间隔着湍急的河流,行动则是架在河上的桥梁." 这篇开始我们学习串相关的数据结构. 欢迎转载,转载请标明出处: 1.  串的相关概念 1)串(string)是由零个或多个字符组成的有限序列,又名叫字符串. 2)串中含有的字符数据称为串的长度,零个字符的串称为空串(null string),它的长度为零. 3)子串与主串,串中任意个数的连续字符组成的子序列称为该串的子串,相应地,包含子串的串称为主串. 4)子串在主

2、蛤蟆的数据结构笔记之二线性表

2.蛤蟆的数据结构笔记之二线性表 到了笔记二了,每个笔记开头都应该弄个语句激励一下自己和小伙伴. "人生中最重要的不是位置,而是前进的方向" 这次咱们学习表,没错是表.什么表?额,汉字真是博大精深,没错,只是个表.不要想歪了. 欢迎转载,转载请标明出处: 1.  定义 线性表(亦作顺序表)是最基本.最简单.也是最常用的一种数据结构.线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的.线性表的逻辑结构简单,便于实现和操作.因此,线性表

23、蛤蟆的数据结构笔记之二十三串的堆分配实现

23.蛤蟆的数据结构笔记之二十三串的堆分配实现 本篇名言:"人的价值是由自己决定的." 接下去是看下如何用C实现串. 欢迎转载,转载请标明出处: 1.  定义 定义结构体如下,一种字符数组,一个表示数组的长度.以一组地址连续的存储单元存放串值字符序列,但是他们的存储空间是在程序执行过程中动态分配而得. typedefstruct { charch[MAXLEN]; intlen; }SString; 2.  复制 将一个串内容复制到另一个结构体中区. SString StrCopy(S

《Android源码设计模式解析与实战》读书笔记(二十四)

第二十四章.桥接模式 桥接模式也称为桥梁模式,是结构型设计模式之一.桥接模式中体现了"单一职责原则"."开闭原则"."里氏替换原则"."依赖倒置原则"等.同时它也是很实用的一种模式. 1.定义 将抽象部分与现实部分分离,使它们都可以独立地进行变化. 2.使用场景 (1)如果一个系统需要在构建的抽象化角色和具体角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系. (2)对于那些不希望使用继承或因为多层次继承导致系统类

大数据笔记(二十四)——Scala面向对象编程实例

===================== Scala语言的面向对象编程 ======================== 一.面向对象的基本概念:把数据和操作数据的方法放到一起,作为一个整体(类 class) 面向对象的特征: (1)封装 (2)继承 (3)多态 二.定义类: class,没有类的修饰符: public.protected.private class Student1 { //定义属性 private var stuName:String = "Tom" privat

Yii源码阅读笔记(二十四)

Module类中获取子模块,注册子模块,实例化控制器,根据路由运行指定控制器方法的注释: 1 /** 2 * Retrieves the child module of the specified ID. 3 * 取出指定模块的子模块 4 * This method supports retrieving both child modules and grand child modules. 5 * 该方法支持检索子模块和子模块的子模块 6 * @param string $id module

《javascript高级程序设计》笔记(二十四)

最佳实践 (一)可维护性 1.什么是可维护的代码 可理解性   直观性    可适应性    可调适性 2.代码约定 ①可读性 ?缩进 ?注释    每个函数和方法——都应该包含一个注释,描述其目的和用到的算法.陈述事先的假设如参数代表什么,函数是否有返回值. 大段代码——前面应该放一个描述任务的注释 复杂的算法——解析是如何做的 Hack ②变量和函数的命名 ?变量名应该为名词 ?函数名应该以动词开始,如getName(),返回布尔值的函数一般以is开头,如isEnable(). ?变量和函数