KMP算法

KMP算法主要包括两个过程,一个是针对子串生成相应的“索引表”,用来保存部分匹配值,第二个步骤是子串匹配。

部分匹配值是指字符串的“前缀”和“后缀”的最长的共有元素的长度。以“ABCDABD”为例:

  • “A”的前缀和后缀都是空,共有元素长度为0;
  • “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},共有元素长度为1;
  • “ABCDAB”的前缀为{A, AB, ABC, ABCD, ABCDA},后缀为{BCDAB, CDAB, DAB, AB, B},共有元素长度为2;
  • “ABCDABD”的前缀为{A, AB, ABC, ABCD, ABCDA, ABCDAB},后缀为{BCDABD, CDABD, DABD, ABD, BD, D},共有元素长度为0;

所以,生成了如下的部分匹配值表:

搜索词 A B C D A B D
部分匹配值 0 0 0 0 1 2 0

生成部分匹配值表的意义在于匹配时,父串的搜索位置可以向后跳动,而不是每次都只向后移动一位。可以用下面的公式计算向后跳动的位数:

移动位数=已匹配的字符数-对应的部分匹配值

以上是KMP的主要思想,下面是KMP的Java实现:

//生成next数组,表示部分匹配值
public static int[] next (String sub) {
int[] a = new int [sub.length()];
char[] c = sub.toCharArray();
int i = 0, j = 1;
a[0] = -1;
for(; j<sub.length(); j++) {
i = a[j-1];
while (i>=0 && c[j]!=c[i+1]) {
i = a[i];
}
if (c[j] == c[i+1]) {
a[j] = i+1;
} else {
a[j] = -1;
}
}
return a;
} //匹配过程
public static void pattern (String str, String sub, int[] next) {
char[] ch1 = str.toCharArray();
char[] ch2 = sub.toCharArray();
int i = 0, j = 0;
while (i<ch1.length) {
if (ch1[i] == ch2[j]) {
i ++;
j ++;
} else if (j==0) {
i ++;
} else {
j = next[j-1] + 1;
}
}
}

BM算法

  BM算法的基本流程: 设文本串T,模式串为P。首先将T与P进行左对齐,然后进行从右向左比较 ,若是某趟比较不匹配时,BM算法就采用坏字符规则和好后缀规则来计算模式串向右移动的距离,直到整个匹配过程的结束。

首先介绍坏字符规则和好后缀规则。

坏字符规则:

  在从右向左扫描的过程中,若发现某个字符x不匹配,则按下面两种情况讨论:

  • 如果字符x在模式P中没有出现,那么从字符x开始的m个文本显然不可能与P匹配成功,直接跳过该区域即可
  • 如果x在模式P中出现,则以该字符进行对齐。

  可以用下面公式表示:(skip(x)是P右移的距离,m为模式串P的长度,max(x)为字符x在P中最有位置)

  skip(x) = m;  x在P中未出现
      = m-max(x);  x在P中出现

好后缀规则:

  若发现某个字符串不匹配的同时,已有部分字符匹配成功,同样分情况讨论:

  • 模式串中有子串匹配上好后缀,此时移动模式串,让该子串和好后缀对齐即可,如果超过一个子串匹配上好后缀,则选择最靠靠近好后缀的子串对齐

  • 模式串中没有子串匹配上后后缀,此时需要寻找模式串的一个最长前缀,并让该前缀等于好后缀的后缀,寻找到该前缀后,让该前缀和好后缀对齐即可
  • 模式串中没有子串匹配上后后缀,并且在模式串中找不到最长前缀,让该前缀等于好后缀的后缀。此时,直接移动模式到好后缀的下一个字符。

  用数学公式表示:

  shift(j) = min{s|(P[j+1..m]=P[j-s+1..m-s]) && (P[j]!=P[j-s])(j>s), P[s+1..m]=P[1..m](j<=s)}

在BM算法匹配过程中,取skip和shift中较大者作为跳跃的距离。

下面是BM算法的C实现:

//根据坏字符规则做预处理,建立坏字符表
int* MakeSkip (char* ptrn, int plen) {
int i;
//申请256个int的空间,一个字符8位,总共有256中不同的字符
int *skip = (int*) malloc( * sizeof(int));
assert (skip != NULL);
//初始化坏字符表
for (i=; i<; i++) {
*(skip + i) = plen;
}
//给表中需要赋值的单元赋值
while (plen != ) {
*(skip + (unsigned char)*ptrn++) = plen--;
}
return skip;
} //根据好后缀规则做预处理,建立好后缀表
int *MakeShift (char* ptrn, int plen) {
//为好后缀表申请空间
int *shift = (int*) malloc(plen * sizeof(int));
//给好后缀表进行赋值的指针
int *sptr = shift + plen - ;
//记录字符串边界的指针
int *pptr = ptrn + plen - ; assert (shift != NULL);
char c = *pptr;
*sptr = ; while (sptr-- != shift) {
char *p1 = ptrn + plen -, *p2, *p3;
do {
while (p1 >= ptrn && *p1-- != c);
p2 = ptrn + plen -;
p3 = p1;
while (p3 = ptrn && *p3-- == *p2-- && p2 >= pptr);
} while (p3 >= ptrn && p2 >= pptr); *sptr = shift _ plen - sptr + p2 - p3;
pptr--;
}
return shift;
} int BMSearch ( char* buf, int blen, char* ptrn, int plen, int* skip, int* shift) {
int b_idx = plen;
if (plen == )
return ;
//计算字符串是否匹配到了尽头
while (b_idx <= blen) {
int p_idx = plen, skip_stride, shift_stride;
while (buf[--b_idx] == ptrn[--p_idx]) {
if (b_idx < ) {
return ;
}
if (p_idx == ) {
return ;
}
}
skip_stride = skip[(unsigned char)buf[b_idx]];
shift_stride = shift[p_idx];
b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride;
}
return ;
}

Sunday算法

  从头开始匹配,当发现失配的时候就判断子串的后一位在父串的字符是否在子串中存在。如果存在则将该位置和子串中的该字符对齐,在从头开始匹配。如果不存在就将子串向后移动,和父串k+1处的字符对齐,再进行匹配。重复上面的操作直到找到,或父串被找完。

  下面是Sunday算法的C实现:

int SundayMatch(byte* pSrc, int nSrcSize, byte* pSubSrc, int nSubSrcSize)
{
int skip[];
for (int i = ; i < ; i++)
{
skip[i] = nSubSrcSize + ;
} for (int i = ; i < nSubSrcSize; i++)
{
skip[pSubSrc[i]] = nSubSrcSize - i;
} int nPos = ;
while(nPos <= nSrcSize - nSubSrcSize)
{
int j = nSubSrcSize - ;
while(j >= && pSrc[nPos + j] == pSubSrc[j])
{
j--;
}
if (j < )
{
break;
}
nPos = nPos + skip[pSrc[nPos + nSubSrcSize]];
}
return nPos;
}

字符串匹配算法——KMP、BM、Sunday的更多相关文章

  1. 字符串匹配算法--KMP字符串搜索(Knuth–Morris–Pratt string-searching)C语言实现与讲解

    一.前言   在计算机科学中,Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)可在一个主文本字符串S内查找一个词W的出现位置.此算法通过运用对这个词在不匹配时本身就包含足够的信息 ...

  2. 字符串匹配算法之BM算法

    BM算法,全称是Boyer-Moore算法,1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了一种新的字符串匹配算法. BM算法定义了两个规则: ...

  3. 字符串匹配 扩展KMP BM&Sunday

    复杂度都是O(n) 扩展1:BM算法 KMP的匹配是从模式串的开头开始匹配的,而1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了一种新的字符串匹 ...

  4. leetcode28 strstr kmp bm sunday

    字符串匹配有KMP,BM,SUNDAY算法. 可见(https://leetcode-cn.com/problems/implement-strstr/solution/c5chong-jie-fa- ...

  5. 字符串匹配算法 - KMP

    前几日在微博上看到一则微博是说面试的时候让面试者写一个很简单的字符串匹配都写不出来,于是我就自己去试了一把.结果写出来的是一个最简单粗暴的算法.这里重新学习了一下几个经典的字符串匹配算法,写篇文章以巩 ...

  6. 字符串匹配算法——KMP算法学习

    KMP算法是用来解决字符串的匹配问题的,即在字符串S中寻找字符串P.形式定义:假设存在长度为n的字符数组S[0...n-1],长度为m的字符数组P[0...m-1],是否存在i,使得SiSi+1... ...

  7. 4种字符串匹配算法:KMP(下)

    回顾:4种字符串匹配算法:BS朴素 Rabin-karp(上) 4种字符串匹配算法:有限自动机(中) 1.图解 KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R ...

  8. 字符串匹配算法KMP算法

    数据结构中讲到关于字符串匹配算法时,提到朴素匹配算法,和KMP匹配算法. 朴素匹配算法就是简单的一个一个匹配字符,如果遇到不匹配字符那么就在源字符串中迭代下一个位置一个一个的匹配,这样计算起来会有很多 ...

  9. 字符串匹配算法(二)-BM算法详解

    我们在字符串匹配算法(一)学习了BF算法和RK算法,那有没更加高效的字符串匹配算法呢.我们今天就来聊一聊BM算法. BM算法 我们把模式串和主串的匹配过程,可以看做是固定主串,然后模式串不断在往后滑动 ...

随机推荐

  1. poj 2942--Knights of the Round Table (点的双连通分量)

    做这题简直是一种折磨... 有n个骑士,骑士之间相互憎恨.给出骑士的相互憎恨的关系. 骑士要去开会,围成一圈坐,相互憎恨的骑士不能相邻.开会骑士的个数不能小于三个人.求有多少个骑士不能开会. 注意:会 ...

  2. 教程-Delphi设置功能表

    1.锁定窗体上的控件,禁止移动位置 D7-Edit>Lock Controls 2.设置控件永久显示名字 D7-Tools>Environment Options>Designer& ...

  3. ServletContext获取项目真实路径

    import javax.servlet.ServletContext; import org.springframework.web.context.ServletContextAware; /** ...

  4. 怎么从代码中拿到栈回溯信息(call stack trace)

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:怎么从代码中拿到栈回溯信息(call stack trace).

  5. 浅谈android4.0开发之GridLayout布局

    作者:李响 本文重点讲述了自android4.0版本号后新增的GridLayout网格布局的一些基本内容,并在此基础上实现了一个简单的计算器布局框架.通过本文,您可以了解到一些android UI开发 ...

  6. 征服 Nginx + Tomcat【转】

    征服 Nginx + Tomcat Server Architecture/Distributed nginxtomcatsessioncluster  2年前一直折腾Apache,现如今更习惯Ngi ...

  7. android105 jni概念

    JNI(Java Native Interface,JAVA原生接口) ,通过JNIjava代码可以调用C代码,JNI在安卓中用的很多.安卓中的框架层就是用过JNI访问类库层的.Iphone是用C/C ...

  8. eclipse下修改项目名导致tomcat内发布名不一致的解决方法 .

    eclipse下修改项目名导致tomcat内发布名不一致的解决方法 . ------------------------------------------------------- 解决方案: 直接 ...

  9. Android 开发第二天

    开发入门HelloWorld 首先打开开发工具 第一步 第二步 效果图 以后可以点击一直下去 第三步骤介绍一下里面项目的作用 SRC是用来保存源代码的东西MainAcrivity.java主视图res ...

  10. iOS之KVO和KVC

    概述 由于ObjC主要基于Smalltalk进行设计,因此它有很多类似于Ruby.Python的动态特性,例如动态类型.动态加载.动态绑定等.今天我们着重介绍ObjC中的键值编码(KVC).键值监听( ...