在对字符串的操作中,我们经常要用到子串的查找功能,我们称子串为模式串,模式串在主串中的查找过程我们成为模式匹配,KMP算法就是一个高效的模式匹配算法。KMP算法是蛮力算法的一种改进,下面我们先来介绍蛮力算法。

  蛮力算法使用两个int型变量当做当前匹配位置的指针,我们假设主串的位置指针为i,模式串的位置指针为j。蛮力算法的策略便是在i和j所指的位置的字符相等时,继续向后匹配,当发生失配时,便将i回溯到本次匹配前位置的后一个位置,而将j设置为0,从而对所有位置完成逐一比对,通过观察i和j是否越界判断整体匹配是否成功,若模式串位置指针j越界,显然此前所有位置都已完全匹配,那么也就可以返回i-j也即完全匹配时主串中匹配子串的下标位置。

int brute(char * mString ,char * subString) {
int i = ,j = ;
int m = strlen(mString),
n = strlen(subString);
while ( i < m &&j < n) {
if (mString[i] == subString[j]) {
i++;
j++;
}
else {
i -= j - ;
j = ;
}
}if (j >= n)
return i - j;
     if(i>=m)
return -1;
}

  通过观察,我们不难发现,如果主串中有大量与模式串相似的字符,从而每次比对都要比较到模式串的最后一个字符才发生失配,从而在每个比较位置都与模式串进行模式串长度n次的比较,而一共有主串长度m次的比较位置。从而时间复杂度达到了O(m*n)。这种情况在串中字符的种类较少时尤其容易发生,如主串为“00000000000001”,模式串为“00001”。

  显然,在每次发生失配时,i指针都要回溯到原来位置的下一个位置,而j指针则是复位至0,一切又从下一个位置从新开始。然而这种做法其实浪费了大量之前比较时所获得的有用信息。在发生失配时,可以分为两种情况:主串失配字符位置i之前的若干字符如果和模式串中的某个真前缀相同(为了避免丢失信息,必须选择最长的一种情况),那么显然只需将模式串与主串中对应字符对齐,而在当前位置与模式串的这个真前缀后的那个字符进行比较即可;而如果不能找到这样的情况,显然主串失配位置前的字符都无法派上用场,从而直接将模式串的开头与当前位置对齐并进行比较即可(注意,如果这种情况中开头位置依然失配,只需让i自增,从下一个位置开始和整个模式串的匹配即可)。这两种情况中的i指针始终没有回溯。因此在最坏情况下的时间复杂度也不会超过O(m)。

int KMP(char *mString ,char* subString) {
int i = ,j = ;
int mlen = strlen(mString);
int slen = strlen(subString);
int* next = (int*)malloc(sizeof(int)*strlen(subString));
getNext(subString, next);
while (i < mlen && j < slen) {
if (mString[i] == subString[j]) i++, j++;
else {
if (j == )i++;
j = next[j];//子串指针前移至最长公共前后缀的下标处
}
}
if(j>=slen)
return i - j;
if (i >= mlen)
return -;
}

  细心的读者可能发现,这里与蛮力算法不同的是多了一次主串适配位置之前字符与模式串前缀字符的比较操作,这样看来似乎复杂度没有改善,其实不然,由于发生失配时主串当前位置i和模式串当前位置j之前的所有字符必然相等,所以这种比较操作只取决于模式串,也就是说我们只需找出模式串每个位置的最长公共前后缀即可。我们只需在比较之前对模式串进行分析处理,将对应信息存入next[]数组来制表以供查询即可将这种操作简化为O(1)的复杂度。而这种预处理操作实际上只需O(n)的复杂度。

  next[]数组的获取我们可以使用递推的策略完成,由于next[]数组中存放的数值为当前位置最长公共前后缀的长度,也即最长公共前缀之后一个字符的下标位置,显然如果之前位置字符与之前位置的最长公共前缀的下一个字符相同,那么当前位置的最长公共前后缀的长度只需增加一即可(特别的,如果当前位置和当前位置最长公共前后缀的后一个字符相等,说明这次比较必然失败,于是应该取其此处不相等的最长公共前后缀);而如果这两个字符不同,我们希望找到之前字符稍短的一个公共前后缀,也即在之前字符的next[]值上再取一次next[]的值,再比较这个值处的字符和之前位置处字符,如果相等取此值加一即可,如果不相等,则继续循环,由于next[]数组中的值必然比数组内值小,所以循环一直继续下去必然收敛于0。当值为0时。取当前值为0即可。

void getNext(char * string, int * next) {
int len = strlen(string);
int i = ,j = ;
next[] = ;
while (j < len) {
if (i == )
next[++j] = ;
if (string[j] == string[i]) {
i++; j++;
         next[j] = string[j]!=string[i]?i:next[i];//避免出现重复比较
}
else
i = next[i]; }
}

纯属个人理解,如有错误,欢迎指出

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

  1. 串的模式匹配,KMP算法

    串的模式匹配 现考虑一个常用操作,在字符串s(我们称为主串)中的第pos开始处往后查找,看在主串s中有没有和子串p相匹配的的,如果有,则返回字串p第一次出现的位置. 暴力求解 int Index(ch ...

  2. 《数据结构》之串的模式匹配算法——KMP算法

    //串的模式匹配算法 //KMP算法,时间复杂度为O(n+m) #include <iostream> #include <string> #include <cstri ...

  3. 串的应用与kmp算法讲解--学习笔记

    串的应用与kmp算法讲解 1. 写作目的 平时学习总结的学习笔记,方便自己理解加深印象.同时希望可以帮到正在学习这方面知识的同学,可以相互学习.新手上路请多关照,如果问题还请不吝赐教. 2. 串的逻辑 ...

  4. 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案

    之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...

  5. 【模式匹配】KMP算法的来龙去脉

    1. 引言 字符串匹配是极为常见的一种模式匹配.简单地说,就是判断主串\(T\)中是否出现该模式串\(P\),即\(P\)为\(T\)的子串.特别地,定义主串为\(T[0 \dots n-1]\),模 ...

  6. 模式匹配的KMP算法详解

    这种由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现的改进的模式匹配算法简称为KMP算法.大概学过信息学的都知道,是个比较难理解的算法,今天特把它搞个彻彻底底明明白白. 注意到这 ...

  7. 字符串模式匹配之KMP算法的next数组详解与C++实现

    相信来看next数组如何求解的童鞋已经对KMP算法是怎么回事有了一定的了解,这里就不再赘述,附上一个链接吧:https://www.cnblogs.com/c-cloud/p/3224788.html ...

  8. 串的模式之kmp算法实践题

    给定两个由英文字母组成的字符串 String 和 Pattern,要求找到 Pattern 在 String 中第一次出现的位置,并将此位置后的 String 的子串输出.如果找不到,则输出“Not ...

  9. 模式匹配之Kmp算法

    Kmp: 算法定义借鉴wikipedia: http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm#KMP_ ...

随机推荐

  1. 对JavaScript中this的理解

    JavaScript中的this其实没传说中的那么难,也没那么乱. 我们来分析下,this主要是跟它的执行环境有关. 而通常情况下,this都是放在函数体中或可执行的JS代码中(函数体除外). 至于J ...

  2. ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

    .NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中.对于分布式缓存,.NET Core提供了针对 ...

  3. java多线程安全问题-同步修饰符于函数

    上一篇文章通过卖票使用同步代码块的方法解决安全问题本篇文章首先探讨如何找出这样的安全问题,并提出第二种方式(非静态函数synchronized修饰)解决安全问题 /* 需求: 银行有一个公共账号金库 ...

  4. bootstrap-导航总结

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. css中的text-overflow

    css中的text-overflow HTML中: <body><div class="clip">此处中多余的文字直接被切掉,不显示</div> ...

  6. Baidu图表插件--Eharts使用(柱状图)

    官网链接:http://echarts.baidu.com/index.html 官网的demo,api都很详细:我就分享下我的学习步骤 首先定义一个显示图标的区域: <div id=" ...

  7. 关于企业选取ERP软件的建议

    笔者以前在广州的一家叫速达软件的公司从事实施维护的工作,该公司是一家专注于ERP软件的公司,主要做进销存与财务的管理软件. 期间也对于各大企业有过一些接触,看到很多公司在购买这款软件之后与这家公司发生 ...

  8. TCP报文段的首部格式

    首部20个字节的的固定部分,40个字节的可选部分 (1)源端口和目的端口 (2)序号(是字节流的编号,0-2^32-1),如果超过了mod2^32 (3)确认号:目的主机希望源主机收到的下一个字节序号 ...

  9. 爬虫:把廖雪峰的教程转换成 PDF 电子书

    写爬虫似乎没有比用 Python 更合适了,Python 社区提供的爬虫工具多得让你眼花缭乱,各种拿来就可以直接用的 library 分分钟就可以写出一个爬虫出来,今天就琢磨着写一个爬虫,将廖雪峰的 ...

  10. Javascript基本概念(一)

    JavaScript基本语法: ECMAScript的语法大量借鉴了C以及其他类C语言的语法. ECMAScript中的一切(变量.函数名.操作符)都区分大小写. 标识符: 含义:指变量.函数.属性的 ...