字符串查找-普通方法和KMP(普拉特操作)介绍
来源:互联网 发布:制作视频剪辑的软件 编辑:程序博客网 时间:2024/04/29 19:14
普通的字符串匹配方法(串的模式匹配方法)
对于普通的串匹配方法,通过简单的例子进行解析
T: a b a c a a b a c a b a c
W: a b a c a b
会从T[0]跟W[0]进行匹配,如果相等则匹配W的下一个字符,直到出现不相等的情况。简单丢弃W[0]开始的匹配信息,然后从T[1]开始继续同W进行匹配,直到串结束或者满足W长度和T长度差值的循环,如果T中不够n个W自然可以结束。
具体实例如下所示,返回第一个匹配串及其后边的字符,时间复杂度O((m-n)*n):
const char* strFind(const char* string1, const char* substring){assert(string1 != NULL && substring != NULL);int m = strlen(string1);int n = strlen(substring);if (m < n)return NULL;for (int i = 0; i <= m - n; ++i){int j = 0;for (; j < n; ++j)//对substring循环,也就是上文中的W{if (string1[i + j] != substring[j])break;}if (j == n)return (string1 + i);}return NULL;}
这种简单的丢弃前面的信息造成了极大的浪费和低下的匹配效率。
KMP算法是由D.E和V.R同时发现,当前算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体就是实现next()函数,函数本身包含了模式串的局部匹配信息。
针对第一种基本匹配方法,KMP方法对于每一个模式串会事先计算出模式串内部匹配信息,在匹配失败时最大移动模式串,以减少匹配次数。
如何计算右移距离成了算法的核心,右移距离的计算也就是next的计算,也就是输入串前缀后缀对应的匹配值的计算。
什么是前后缀?
“前缀”指除了最后一个字符以外,一个字符串的全部头部组合,“后缀”除了第一个字符以外,一个字符串的全部尾部组合。实例如下:
字符串:bread
前缀:b, br, bre, brea
后缀:read, ead, ad, d
部分匹配值的计算:
搜索词:A B C D A B D
部分匹配值就是“前缀”和“后缀”的最长共有元素的长度。根据以上搜索词为例:
A的前缀和后缀都为空集,共有元素的长度为零。
AB的前缀为A,后缀为B,共有元素长度为0。
ABC前缀为A, AB,后缀为BC,C共有元素长度为0.
ABCD前缀为A,AB,ABC,后缀为BCD,CD,D,共有元素长度为0。
ABCDA前缀为A,AB,ABC,ABCD,后缀为BCDA,CDA,DA,A共有元素为A,共有元素长度为1。
ABCDAB前缀A,AB,ABC,ABCD,ABCDA,后缀为BCDAB,CDAB,DAB,AB,B。共有元素为AB,长度为2.
部分匹配的实质是:有时候字符串头部和尾部会有重复,例如“ABCDAB”之中有两个“AB”,那么他的部分匹配值就是2(“AB”的长度)。搜索移动的时候第一个AB向后移动思维(字符串长度减去部分匹配值),第一个AB向后移动四位就可以到达尾部AB的位置。
得到:移动位数 = 字符串长度 – 部分匹配值。字符串长度和部分匹配值的求解分别都是针对已经匹配的模式串子串进行的求解,实例如下:
T: a b a c a a b a c a b a c
W: a b a c a b
T[5]与W[5]出现不匹配,T[0]—T[4]是匹配的。字符串长度为5,
前缀:a,a b,a b a,a b a c; 后缀:b a c a,a c a,c a,a; 共有元素为a,长度为1。
因此可求的移动位数为4。
Next求解代码:
void get_nextval(char const* ptrn, int plen, int* nextval){int i = 0;nextval[i] = -1;int j = -1;while(i < plen - 1){if (j == -1 || ptrn[i]==ptrn[j]){++i;++j;nextval[i] = j;} else{j = nextval[j];}}}
字符串查找实例如下所示:
T: a b a c a a b a c a b a c a b a a b b
W: a b a c a b
可求得W的next表为:
W
a
b
a
c
a
b
0
0
1
0
1
2
关于next求解,加入第二项的a,其实就是求的aba的前缀后缀公共项的最大值。
a b a c a a b a c a b a c a b a a b b
a b a c a b
从第0项开始对比,直到找到不相等的项:发现T[5]和W[5]不相等,也就是说T[0]-T[4]相等,获取T[0]-T[4]的前缀后缀相同项,最大长度,查表可得为1.
移动位数 = 5 – 1 = 4;
a b a c a a b a c a b a c a b a a b b
a b a c a b
按照此规律反复执行。时间复杂度O(m+n)
代码如下:
int kmp_search(const char*src,int slen,const char* patn, int plen,const int* nextval, int pos){int i = pos, j = 0;while(i<slen && j<plen){if (j == -1 || src[i] == patn[j]){++i;++j;}else{j = nextval[j];}}if (j >= plen){return i-plen;} else{return -1;}}
- 字符串查找-普通方法和KMP(普拉特操作)介绍
- 克努特-莫里斯-普拉特操作(简称KMP算法)
- 克努特-莫里斯-普拉特操作(KMP算法)
- KMP算法(克努特-莫里斯-普拉特操作)简介
- 4.3.2 KMP克努特-莫里斯-普拉特操作
- 字符串查找算法BF和KMP
- 字符串匹配(java)实现,普通的匹配和KMP算法 (参考)
- KMP算法和普通算法字符串匹配差距
- 字符串的查找:朴素查找算法和KMP算法
- 字符串查找——朴素查找和kmp算法
- Java 查找方法(普通查找,二分查找)
- 查找字符串中出现最多的字符和个数(两种方法一个普通方法 一个是正则表达式方式)
- KMP(字符串查找)(hihocoder)
- 字符串查找(KMP算法及其优化)
- LintCode 字符串查找(暴力法+KMP)
- KMP字符串查找
- KMP算法查找字符串
- 字符串查找之KMP
- 【MyEclipse】——MyEclipse has detected that less than 5% of the 28M of PS Survivor Space...
- RT-thread main函数分析
- JavaScript - ajax请求的初步封装
- gitHub如何切换到非master分支的其他分支
- RT-thread finsh组件工作流程
- 字符串查找-普通方法和KMP(普拉特操作)介绍
- Unicode、UTF-8 和 ISO8859-1到底有什么区别
- Struts2的简单总结(4)
- 逆序对问题
- RT-thread 设备驱动组件之PIN设备
- javaScript基础学习(7)(获取方法名)
- keras-学习笔记
- Linux下wget下载整个FTP目录(含子目录)
- 聊聊HTTPS和SSL/TLS协议