数据结构8————串的BF匹配模式和KMP匹配模式
来源:互联网 发布:淘宝怎么设置客服接待 编辑:程序博客网 时间:2024/06/05 08:00
数据结构8————串的BF匹配模式和KMP匹配模式
一.前面一些絮絮叨叨的话
1.名称解释
- 子串:串中任意连续字符组成的子序列组成的
- 主串:包含子串的相应的串
- 前缀子串:S=a1…ab U=a1….an 当1<=n< b时,称U为S的前缀子串
- 后缀子串:S=a1…ab U=an….ab 当1 < n < b时,称U为S的后缀子串
例:
S:abaabca
S的前缀子串:a ab aba abaa abaab abaabc
S的后缀子串:a ca bca abca aabca baabca
- 匹配模式:计算主串中子串的位置。
2.博客内容概要
- BF匹配模式(蛮力匹配)
- KMP匹配模式
- KMP匹配模式优化
3.其他
- 文中所有代码只是核心部分,完整部分见文末的链接
二.BF匹配模式
1.思路
- 思路很简单,从主串的第一位开始和子串第一位比较。
- 如果相同,继续比较子串和主串的下一位,如果将子串比较完毕都成功,说明匹配成功,子串在主串第一位。
- 如果中间有一个不匹配,i回退到上次匹配首位的下一位,j回退到0。
2.图示:
- 主串:goodgoogle
- 子串:google
3.代码
#include <stdio.h>//str1-->主串,str2-->子串。//返回子串在主串第pos个字符之后的位置。 如果不存在,返回-1 int Index(char *str1,char *str2,int pos){ int i=pos; int j=0; while(str1[i]&&str2[j]){ if(str1[i]==str2[j]){ //如果此时主串和子串相等,继续向下比较 i++; j++; }else{ i=i-j+2; //如果不相等 i回退到上次匹配的首位。 j=0; //j回退到子串的首位。 } } if(str2[j]==0) //如果退出循环时,j指向子串末尾。则说明匹配成功 return i-j; else return -1; }int main(void){ char str1[100]; char str2[30]; printf("请输入主串:\n"); gets(str1); printf("请输入子串\n"); gets(str2); printf("%d\n",Index(str1,str2,0));}
4.性能分析
- 主串:abcdefgh…(后面还有)
- 子串:abcdefX
下图是按照BF模式匹配的示意图。
仔细研究就会发现,在BF模式中,i的回溯很浪费时间,上一次的匹配所得结果完全没有用上。如果i不回溯可以吗?
- 在这个匹配中,是完全可以的,子串的首位是a,而上次匹配到子串的X前失败,说明子串X前和主串f前的字符都是一样的,因为子串6个字符都是不同的,所以主串f前5个也是不同的,所以主串中a–f之间肯定没有a。
所以②~⑤完全是不必要的。可以直接①–>⑥
在这次匹配中由于子串很特殊,可以不用回溯i,如果子串没那么特殊,子串中含有重复的字符,那也可以做的不回溯i吗?
- 是的,可以的。可以做到这一点的就是KMP匹配模式。
三. KMP匹配模式
1.思路
- 思路和BF模差不多。
- 不同的地方在于,匹配失败后i不回溯,只回溯j。
- 匹配失败后,不同失败的地方j回溯的位置不同。所以将每个位置匹配失败后,j回溯的位置存入next数组。
- 所以KMP的重点是next数组的求解。
2.next数组的求解
- 首先我们来试一下,不同子串匹配失败后,j应该回溯到哪?
- 有没有发现一些什么规律?
- 我呢发现,回溯的长度=子串前部和后部分一样的长度中最大值。
- 标准说法是:(匹配失败字符前面的所有字符构成)子串的前缀表达子串和后缀子串中相同子串的最长的长度。
例:ababax
前缀子串:a ab aba abab
后缀子串:b ba aba baba
公共子串:aba
所以j回溯到3
3.next代码
//计算子串str2的next数组void get_next(char *str2,int *next){ int i,k; i=0; k=-1; next[0]=-1; while(str2[i]){ if(k==-1||str2[i]==str2[k]){ //str2[i]-->后缀的单个字符 i++; //str2[j]-->前缀的单个字符 k++; next[i]=k; }else{ k=next[k]; } }}
4.next代码解释
求解ABABABCX中X对应的next值。
5.核心代码
代码和BF差不多
int Index_KMP(char *str1,char *str2,int pos){ int i=pos; int j=0; int next[100]; get_next(str2,next);//计算子串的next数组 printf("\n子串next数组:\n"); int k; for(k=0;str2[k];k++) printf("%d ",next[k]); while((str1[i]&&str2[j])||j==-1){ if(j==-1||str1[i]==str2[j]){ //如果此时主串和子串相等,继续向下比较 i++; j++; }else{ //i=i-j+2; //i不需要回溯 //j=0; j=next[j]; //j回退到对应的next中的值 } } if(str2[j]==0) //如果退出循环时,j指向子串末尾。则说明匹配成功 return i-j; else return -1; }
6.性能分析
比起BF算法来说,KMP算法的i不用回溯,优化了很多,但它真的完美吗?我们来看个例子。
* 主串:aaaaafgh
* 子串:aaaaax
* 上图就是按照KMP算法进行匹配时的示意图。可以看出来j从5回溯到4,然后回溯到3,然后2.。。到最后回溯到0。
* 在这个子串中,其实j回溯到321完全没有必要。子串X前5个字符相同。当f和第五个a匹配失败后,那么f和前4个a完全没有必要匹配。所以j应该直接从②到⑥。
* 在这里比较极端的例子中,我们发现KMP中j的有些回溯是完全没有必要的。那么该如何减少一些不必要的j回溯呢?
四.KMP匹配模式的优化
1.思路
- 我们发现,如果j回溯后和j回溯前的两个字符相同,那么这一次回溯四完全没有必要的。(j回溯前匹配失败,回溯后肯定也是失败的)
- 我们将优化的KMPj需要回溯的值放入nextval中
- nextval和next相比,多了一个条件,假设str字符串中字符c的nextval计算的是k,如果str[k]==c,那么c的naxtval=nextval[k]。
ababa
计算第二个a的nextval值
a的naxtval是0,因为naxtval[0] == ‘a’,所以a的nextval=nextval[0]=-1
2.求naxtval值的代码
#include <stdio.h>//计算子串str2的next数组void get_next(char *str2,int *nextval){ int i,k; i=0; k=-1; nextval[0]=-1; while(str2[i]){ if(k==-1||str2[i]==str2[k]){ //str2[i]-->后缀的单个字符 i++; //str2[j]-->前缀的单个字符 k++; if(str2[i]!=str2[k]) nextval[i]=k; else nextval[i] = nextval[k];//当前字符和前缀字符相同,则将前缀 //nextval值赋给nextval在i的位置 }else{ k=nextval[k]; } }}
五.源代码
test8中
阅读全文
0 0
- 数据结构8————串的BF匹配模式和KMP匹配模式
- 串的模式匹配——KMP && BF
- 数据结构- 串的模式匹配算法:BF和 KMP算法
- 数据结构- 串的模式匹配算法:BF和 KMP算法
- 串模式匹配——从BF到KMP最精讲
- (五)串的模式匹配——BF算法和KMP算法
- 串的模式匹配算法:BF和KMP算法
- 串的模式匹配算法:BF和 KMP算法
- 数据结构 模式匹配BF和KMP算法实现
- 串的模式匹配之 Brute—Force(BF)
- 数据结构——串(朴素的模式匹配算法、KMP模式匹配算法)
- 模式匹配算法BF和KMP
- 【数据结构与算法】模式匹配——从BF算法到KMP算法(附完整源码)
- 【数据结构与算法】模式匹配——从BF算法到KMP算法(附完整源码)
- 字符串的模式匹配(BF、KMP)
- 数据结构——串的朴素模式和KMP匹配算法
- 数据结构——串的朴素模式和KMP匹配算法
- 【字符串】模式匹配:BF / KMP
- 机械波简述------说说纵波(声波是一种纵波)
- android图片状态栏实现沉浸式状态栏
- 无锁队列
- 危桥僵尸和创新思维
- Hive的分桶详解
- 数据结构8————串的BF匹配模式和KMP匹配模式
- 关键字--exists用法
- [USACO 2013 Jan]Island Travels
- 单例详解
- 抽象工厂方法
- 开发日记 X
- 排列(康托展开)
- 理解与配置Android studio中的gradle
- 数据结构: 单链表排序