
第四章 串、数组和广义表 —— BF算法和KMP算法
本节讲的是字符串匹配算法。
发布日期:2021-05-06 10:56:16
浏览次数:8
分类:技术文章
本文共 4112 字,大约阅读时间需要 13 分钟。
BF算法和KMP算法
文章目录
BF算法
代码如下:
#include#define OK 1#define ERROR 0#define MAXSTRLEN 255typedef int Status;typedef int ElemType;typedef unsigned char SString[MAXSTRLEN + 1]; //0号单元存放串的长度using namespace std;Status StrAssign(SString T,char chars[]){ //赋值 //生成一个其值等于chars的串T。 int i; if(strlen(chars)>MAXSTRLEN) return ERROR; T[0]=strlen(chars); for(i=0; i<=T[0]; i++) { T[i+1]=chars[i]; } return OK;}int BF(SString S,SString T,int pos){ int i = pos; int j = 1; if (pos<1) { cout<<"输入的位置不合理"< T[0]&&T[0]) cout<<"子串在主串中首字母的位置为"< < > a; StrAssign(S, a); cout<<"请输入要查询的子串:"; cin>>b; StrAssign(T, b); cout<<"请输入从子串的哪个位置之后开始查询:"; cin>>pos; BF(S,T,pos); return 0;}
KMP算法
KMP算法就是尽可能的把BF算法中的无意义的部分去掉。时间复杂度为 O ( m + n ) O(m+n) O(m+n).
- 主串的指针 i i i 不回溯
- 当在 P j P_j Pj处失配时,模式串的指针 j j j 不回溯到1,而是根据已匹配的区间( P 1 P_1 P1 —— P j − 1 P_{j-1} Pj−1 )的前后缀重合部分尽可能的向右移动。上图回溯到 P k P_k Pk处。
KMP 算法的难点就在于去找到当主串在 S i S_i Si处失配后应该跟模式串的哪一个位置再开始匹配。这便是Next函数去解决的问题了。
Next函数的思路:
1.定义next[1] = 0.这个是为了方便函数的实现定的一个规定,之后你就知道奥妙了。 2.当模式串在 j j j 处跟主串失配后去看模式串已匹配区间[1, j-1]的最大相等前后缀的值,然后把这个值加1就是模式串在失配处的next值了。eg:

- 当 j = 1 j = 1 j=1时我们规定next[j] = 0
- 当 j = 4 j = 4 j=4时我们看已匹配区间[1,3]的最大相等前后缀。这里“abc”的最大相等前后缀为0,加一之后为1.所以没有重合部分时我们只能老老实实的从第一个开始比较了。
- 当 j = 5 j = 5 j=5时我们看已匹配区间[1,4]的最大相等前后缀。这里“abca”的最大相等前后缀为1(a),加一之后为2.所以当位置5失配时我们让主串的第5个位置跟模式串的第2个位置比较,这样正好主串的第4个位置跟模式串的第1个位置都是a。
- 当 j = 12 j = 12 j=12时我们看已匹配区间[1,11]的最大相等前后缀。这里“abcaabbcabc”的最大相等前后缀为3(abc),加一之后为4.所以当位置12失配时我们让主串的第12个位置跟模式串的第4个位置比较,这样正好主串的第9,10,11个位置跟模式串的第1,2,3个位置都是abc。 ……
Next函数就是为了把模式串的每一个位置失配后要移动到的下一个位置保存在next数组里。
void Next(SString T,int next[]){ int j = 1; int k = 0; next[1] = 0; while(j
假设模式串为“abab”我们来根据代码跑一边。
1.最开始k = 0.所以!k肯定成立。执行if语句:j = 2,k = 1,next[2] = 1. 2.继续while循环,此时if条件不成立,k = next[1] = 0. 3.继续while循环,此时!k成立。执行if语句:j = 3,k = 1,next[3] = 1. 4.继续while循环,此时T[3] == T[1]成立。执行if语句:j = 4,k = 2,next[4] = 2.这个正好就是和咱们之前讲的next[ j ] 等于已匹配区间[ 1 , j-1 ]的最大相等前后缀的值加一。
到此如果还没看懂可以看下这个:
完整代码如下:
#include#define OK 1#define ERROR 0#define MAXSTRLEN 255typedef int Status;typedef int ElemType;typedef unsigned char SString[MAXSTRLEN + 1]; //0号单元存放串的长度using namespace std;Status StrAssign(SString T,char chars[]){ //赋值 //生成一个其值等于chars的串T。 int i; if(strlen(chars)>MAXSTRLEN) return ERROR; T[0]=strlen(chars); for(i=0; i<=T[0]; i++) { T[i+1]=chars[i]; } return OK;}int KMP(SString S,SString T,int pos,int next[]){ int i = pos; int j = 1; if (pos<1) { cout<<"输入的位置不合理"< T[0]) { cout<<"子串在主串中首字母的位置为:"< > a; StrAssign(S, a); cout<<"请输入要查询的子串:"; cin>>b; StrAssign(T, b); cout<<"请输入从子串的哪个位置之后开始查询:"; cin>>pos; int next[T[0]+1]; Next(T,next); KMP(S,T,pos,next); return 0;}
Next函数优化Nextval
假如在某处字符a失配,根据next算好的跳转位置后还是字符a,那显然还是失配的。Nextval就是来把这些无意义的步骤给省去的。
eg:

- 在位置3处失配,由next数组得跳转到位置1.但是位置1处还是个a,失配后再跳转到位置0.如果用nextval数组我们就可以直接跳转到位置0处,省去跳转到位置1这一步。
- 所以nextval的求法就是在next的基础上看跳转后的字符是否相等,如果相等那么就用之前的nextval值来当这个的nextval。如果不相等那就用它本身的next当nextval。
这个实现起来也很简单,加一个if判断就ok了。代码如下:
#include#define OK 1#define ERROR 0#define MAXSTRLEN 255typedef int Status;typedef int ElemType;typedef unsigned char SString[MAXSTRLEN + 1]; //0号单元存放串的长度using namespace std;Status StrAssign(SString T,char chars[]){ //赋值 //生成一个其值等于chars的串T。 int i; if(strlen(chars)>MAXSTRLEN) return ERROR; T[0]=strlen(chars); for(i=0; i<=T[0]; i++) { T[i+1]=chars[i]; } return OK;}int KMP(SString S,SString T,int pos,int nextval[]){ int i = pos; int j = 1; if (pos<1) { cout<<"输入的位置不合理"< T[0]) { cout<<"子串在主串中首字母的位置为:"< > a; StrAssign(S, a); cout<<"请输入要查询的子串:"; cin>>b; StrAssign(T, b); cout<<"请输入从子串的哪个位置之后开始查询:"; cin>>pos; int nextval[T[0]+1]; Nextval(T,nextval); KMP(S,T,pos,nextval); return 0;}
发表评论
最新留言
路过按个爪印,很不错,赞一个!
[***.219.124.196]2025年03月11日 07时19分41秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
大数据学习之Spark——03Spark代码初体验(Word Count)
2019-03-03
LeetCode0234. 回文链表
2019-03-03
比特币史话·78 | 有容乃大(2): 零食售卖机
2019-03-03
比特币史话·96 | 隐私(3): 熔币重铸
2019-03-03
Filecoin主网上线,它是谁、割了谁?
2019-03-03
一切不能跑赢比特币的勤奋,都是愚蠢的努力
2019-03-03
Fire prejudice: 巴菲特搭档芒格首度认可比特币
2019-03-03
GLUT和wxWidgets在OpenGL开发中的比较
2019-03-03
CodeBlocks开发wxWidgets环境配置详细
2019-03-03
Qt 转向 LGPL之后,wxWidgets 路在何方
2019-03-03
[翻译]2009年6月wxWidgets更新 - 支持图标的wxButton
2019-03-03
wxAUI - wxWidgets用户界面框架 - 使用感受
2019-03-03
wxSqlite3 - wxWidgets封装的Sqlite数据库访问类库 - 使用感受
2019-03-03
wxSqlite3 和 wxPropertyGrid 类库的说明
2019-03-03
wxSqlite3类库的使用感受 - 关于乱码的问题
2019-03-03
天涯人脉通讯录 - 设计草图
2019-03-03
秒表计时器(Stopwatch) - V1.1
2019-03-03
★★★男女朋友价格计算器V1.6 - 看看你的朋友值多少钱 :-)
2019-03-03