计算机基础数据结构讲解第十一篇-字符串模式匹配KMP算法第一讲

  本篇讲解串的有关知识,串就是字符串,很常见。和线性表类似,但在基本操作上,通常以子串为操作对象。重点是字符串的模式匹配算法,就是在主串中找到子串。最经典的是KMP匹配算法的原理,要知道算法的原理及next数组的推理过程,求next数组可以先计算出部分匹配值表然后再变形,根据公式求解.改进方法是用nextval数组。

一:简单的模式匹配算法

  子串的定位操作通常称为串的模式匹配,求子串在主串中的位置,最简单的定长存储,是一种暴力的匹配算法。代码如下:

int Index(thisString S,thisSting T){
  int i=1;j=1;
  while(i<=S.length&&j<=T.length){
    if(S.ch[i]==T.ch[j]){
      i++;j++;
    }
    else{
      i=i-j+2;
      j=1;
    }
  }
  if(j>T.length)
    return i-T.length;
  return 0;
}

  暴力匹配的最坏时间复杂度是O(nm),其中n和m为主串和模式串的长度,但这种匹配算法就会将主串前面匹配过的忽略掉,每次匹配失败都是模式后移一位再从头开始比较,比较重复,效率比较低。改进的算法思路是如果已匹配相等的前缀序列中有某个后缀正好是模式的前缀,那么就可以将模式向后滑动到与这些字符对齐的位置,只针对模式子串进行操作,主串指针无需回溯,继续进行比较,这种算法仅与模式串本身结构有关,与主串无关。这种算法也就是KMP算法,下面进行详细介绍这个算法。

二:字符串的前缀,后缀和部分匹配值

1.前缀

  前缀指除了最后一个字符以外,字符串的所有头部子串。

2.后缀

  后缀指除了第一个字符以外,字符串的所有尾部子串。

3.部分匹配值

  部分匹配值为字符串的前缀和后缀的最大相等公共前后缀长度。
比如在'ababa'中,'a'的最大相等公共前后缀长度为0,'ab'最大相等公共前后缀长度为0,依次可得所有子串的最大相等公共前后缀长度,然后就得到部分匹配值为00123,但是部分匹配值有什么作用呢,有一个公式是当某个字符匹配失败时。
可以根据移动位数=已匹配的字符数-对应的部分匹配值(指的是已匹配的字符组的最后一个字符的部分匹配值)。和就相当于把已匹配的字符数的最大相等公共前缀移到后缀的位置。
  这个过程珠串始终没有回退,故KMP算法可以在O(n+m)的时间数量级上完成串的模式匹配操作,大大提高了匹配效率。
  当某趟发生失配时,对应的部分匹配值为0,此次移动的位数最大,直接将子串首字符移动到主串i位置进行下一趟比较。否则有最大相等公共前后缀长度,移动后从i位置进行比较。无论部分匹配值是否为0,都是从主串的第i个位置进行比较。

三:KMP算法具体实现过程

  由上可知,KMP算法的核心就是匹配失败后移动子串的位置,可以认为是子串右移位数进行下一次比较。
  右移位数就是把已匹配的字符数的前缀移动到后缀的位置,也就是
  右移位数=已匹配的字符数-对应的部分匹配值(指的是已匹配的字符组的最后一个字符的部分匹配值)
  即Move = (j-1) - PM[j-1]
  每次匹配失败后,都要找它前一个元素的部分匹配值,这样使用起来就有些不方便,所以将PM表后移一位,这样哪个元素匹配失败,直接看它的部分匹配值即可。这时候要注意两个细节:
  (1)第一个元素后移后空缺的位置用-1填充,因为若是第一个元素匹配失败,则需要将子串向右移动一位,不需要计算移动的位数。
  (2)最后一个元素在右移的过程中溢出,他的部分匹配值是下一位元素使用的,但显然已没有下一个元素,故可以舍去。
  这样就可以把右移位数表达式写为如下:
  右移位数=已匹配的字符数-对应的部分匹配值(子串失配位置的匹配值)
  Move = (j-1) - next[j]
  然后子串的比较指针就回退到:
  j = j - Move = next[j] + 1
  这就是失配时字串的比较指针回退的位置。最终得到j=next[j],含义是当子串的第j个元素失配时,则跳到子串的next[j]位置重新与主串当前位置进行比较。

四:求得next[j]数组

  核心就是得到子串的最大公共前后缀,然后把子串最大公共前后缀的前缀根主串和子串的最大公共前后缀相同的元素对其,再从主串的第i个位置进行比较。
  当模式串已匹配字符序列中不存在最大公共前后缀时,应移动最大位数即j-1位,让主串的第i个字符和模式第一个字符进行比较,此时右移位数最大。
  当模式串第一个字符与主串的第i个字符失配时,规定next[1]=0,可以认为是主串第i个位置和模式串第一个字符的前面空位置对齐,直接将模式串后移,从主串的下一个位置(i+1)和模式串的第一个字符继续比较。这样可以得到next函数公式如下:

在这里插入图片描述

posted @ 2020-09-25 00:27  一只帅气的IT小昂  阅读(373)  评论(0编辑  收藏  举报