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. glance image cache

    The Glance Image Cache The Glance API server may be configured to have an optional local image cache ...

  2. IIS8之System.ServiceModel.Activation.HttpModule錯誤處理

    在Windows Server 2012 R2上安装一个WCF服务,怎么弄都是报System.ServiceModel.Activation.HttpModule錯誤 经过不懈尝次及查找资料,终于找到 ...

  3. Java 随机生成中文姓名,手机号,邮编,住址

    package lovo; import java.util.HashMap; import java.util.Map; /** * 随机生成中文姓名,性别,Email,手机号,住址 * @auth ...

  4. linux安装svn服务器(yum方式)

    1.查看yum是否安装         在终端中输入yum即可如果已经安装,会显示yum的参数         如果没有安装,会提示yum未安装或无效命令…… 2.安装svnyum -y instal ...

  5. mfc开发问题_v1

    1. 设置对话框按钮背景图片? 首先,设置对话框按钮的属性为Bitmap,然后导入资源文件(一个你需要作为背景的小图片),最后在该对话框类的OnInitDialog函数中添加如下代码: //设置对话框 ...

  6. dom4j中对xml的查增

    package dom; import java.io.FileWriter;import java.util.Iterator; import org.dom4j.Document;import o ...

  7. Android(java)学习笔记162:Android启动过程(转载)

    转载路径为: http://blog.jobbole.com/67931/ 1. 关于Android启动过程的问题: 当按下Android设备电源键时究竟发生了什么? Android的启动过程是怎么样 ...

  8. Modify the average program to promote for intergers repeatedly.stop when a nagetive number is entere

    #include<stdio.h> int main(void) { intcount ,sum,aninterger; printf("enterthe interger an ...

  9. Nginx高性能服务器安装、配置、运维 (4) —— Nginx服务、架构及其信号

    五.Nginx服务.架构及其信号 (1)Nginx服务的查看 1.netstat -antp 查看Nginx是否在80端口运行: 2.ps aux|grep nginx 查看nginx相关进程: 发现 ...

  10. js 字符串编码转换函数

    escape 方法 对 String 对象编码以便它们能在所有计算机上可读, escape(charString) 必选项 charstring 参数是要编码的任意 String 对象或文字. 说明 ...