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. [转]JQuery判断浏览器类型版本1.9和2.0之后的区别

    转至:http://zhidao.baidu.com/link?url=Nzk2aSxBKRZKYg9Evqn8hLwMyXTI-4jza-zCAZq4Vd6hWCOHIvuBX6yj8hzDYDrf ...

  2. 关于.net的一些基础知识(二)

    索引器是什么?有什么作用?索引器允许类的实例以访问数组的形式来访问对象里面的属性.如我们经常可以看到类似于dr[“name”]=”test”,或者说以config[“connectString”]来获 ...

  3. 修改Tomcat Connector运行模式,优化Tomcat运行性能

    Tomcat是一个小型的轻量级应用服务器,也是JavaEE开发人员最常用的服务器之一.不过,许多开发人员不知道的是,Tomcat Connector(Tomcat连接器)有bio.nio.apr三种运 ...

  4. centos6.6 虚拟机集群搭建

    1.centosos6.6下载 windows 64位: thunder://QUFodHRwOi8vbGludXguemh1YW5neGl0b25nLmNvbTo4MDgvMjAxNTAxL0Nlb ...

  5. Spring Autowire自动装配

    在应用中,我们常常使用<ref>标签为JavaBean注入它依赖的对象.但是对于一个大型的系统,这个操作将会耗费我们大量的资源,我们不得不花费大量的时间和精力用于创建和维护系统中的< ...

  6. Hyper-V性能-CPU分配

    为新部署的微软Hyper-V环境中的主机和网络挑选合适的硬件并非易事,更不用说在生产环境中衡量和监控性能这项任务了.在这里,我和大家谈谈服务器的核心CPU与Hyper-V的结合是如何相得益彰的. 我接 ...

  7. 特殊的forward_list操作

    为了理解forward_list为什么有特殊版本的添加和删除操作,考虑当我们从一个单向链表中删除一个元素时会发生什么.当添加或删除一个元素时,删除或添加的元素之前的那个元素的后继会发生变化.为了添加或 ...

  8. C#隐式类型

    隐式类型 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys ...

  9. AWS IAM (Identity and Access Management) 使用笔记

    为 AWS 管理控制台登录页面 URL 创建别名 $ aws iam create-account-alias --account-alias <value> 创建用户 $ aws iam ...

  10. Fetch的使用

    import React,{ Component } from 'react'; import { AppRegistry, ListView, Image, Text, StyleSheet, Vi ...