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实现:

  1. //生成next数组,表示部分匹配值
  2. public static int[] next (String sub) {
  3. int[] a = new int [sub.length()];
  4. char[] c = sub.toCharArray();
  5. int i = 0, j = 1;
  6. a[0] = -1;
  7. for(; j<sub.length(); j++) {
  8. i = a[j-1];
  9. while (i>=0 && c[j]!=c[i+1]) {
  10. i = a[i];
  11. }
  12. if (c[j] == c[i+1]) {
  13. a[j] = i+1;
  14. } else {
  15. a[j] = -1;
  16. }
  17. }
  18. return a;
  19. }
  20.  
  21. //匹配过程
  22. public static void pattern (String str, String sub, int[] next) {
  23. char[] ch1 = str.toCharArray();
  24. char[] ch2 = sub.toCharArray();
  25. int i = 0, j = 0;
  26. while (i<ch1.length) {
  27. if (ch1[i] == ch2[j]) {
  28. i ++;
  29. j ++;
  30. } else if (j==0) {
  31. i ++;
  32. } else {
  33. j = next[j-1] + 1;
  34. }
  35. }
  36. }

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实现:

  1. //根据坏字符规则做预处理,建立坏字符表
  2. int* MakeSkip (char* ptrn, int plen) {
  3. int i;
  4. //申请256个int的空间,一个字符8位,总共有256中不同的字符
  5. int *skip = (int*) malloc( * sizeof(int));
  6. assert (skip != NULL);
  7. //初始化坏字符表
  8. for (i=; i<; i++) {
  9. *(skip + i) = plen;
  10. }
  11. //给表中需要赋值的单元赋值
  12. while (plen != ) {
  13. *(skip + (unsigned char)*ptrn++) = plen--;
  14. }
  15. return skip;
  16. }
  17.  
  18. //根据好后缀规则做预处理,建立好后缀表
  19. int *MakeShift (char* ptrn, int plen) {
  20. //为好后缀表申请空间
  21. int *shift = (int*) malloc(plen * sizeof(int));
  22. //给好后缀表进行赋值的指针
  23. int *sptr = shift + plen - ;
  24. //记录字符串边界的指针
  25. int *pptr = ptrn + plen - ;
  26.  
  27. assert (shift != NULL);
  28. char c = *pptr;
  29. *sptr = ;
  30.  
  31. while (sptr-- != shift) {
  32. char *p1 = ptrn + plen -, *p2, *p3;
  33. do {
  34. while (p1 >= ptrn && *p1-- != c);
  35. p2 = ptrn + plen -;
  36. p3 = p1;
  37. while (p3 = ptrn && *p3-- == *p2-- && p2 >= pptr);
  38. } while (p3 >= ptrn && p2 >= pptr);
  39.  
  40. *sptr = shift _ plen - sptr + p2 - p3;
  41. pptr--;
  42. }
  43. return shift;
  44. }
  45.  
  46. int BMSearch ( char* buf, int blen, char* ptrn, int plen, int* skip, int* shift) {
  47. int b_idx = plen;
  48. if (plen == )
  49. return ;
  50. //计算字符串是否匹配到了尽头
  51. while (b_idx <= blen) {
  52. int p_idx = plen, skip_stride, shift_stride;
  53. while (buf[--b_idx] == ptrn[--p_idx]) {
  54. if (b_idx < ) {
  55. return ;
  56. }
  57. if (p_idx == ) {
  58. return ;
  59. }
  60. }
  61. skip_stride = skip[(unsigned char)buf[b_idx]];
  62. shift_stride = shift[p_idx];
  63. b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride;
  64. }
  65. return ;
  66. }

Sunday算法

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

  下面是Sunday算法的C实现:

  1. int SundayMatch(byte* pSrc, int nSrcSize, byte* pSubSrc, int nSubSrcSize)
  2. {
  3. int skip[];
  4. for (int i = ; i < ; i++)
  5. {
  6. skip[i] = nSubSrcSize + ;
  7. }
  8.  
  9. for (int i = ; i < nSubSrcSize; i++)
  10. {
  11. skip[pSubSrc[i]] = nSubSrcSize - i;
  12. }
  13.  
  14. int nPos = ;
  15. while(nPos <= nSrcSize - nSubSrcSize)
  16. {
  17. int j = nSubSrcSize - ;
  18. while(j >= && pSrc[nPos + j] == pSubSrc[j])
  19. {
  20. j--;
  21. }
  22. if (j < )
  23. {
  24. break;
  25. }
  26. nPos = nPos + skip[pSrc[nPos + nSubSrcSize]];
  27. }
  28. return nPos;
  29. }

字符串匹配算法——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. Python模块导入及使用经验回顾 [ 持续更新 ]

    1,若需要导入的模块并不是一个简单的*.py文件,而是在Lib目录下的一个文件夹,则要注意检查这个文件夹下有无__init__.py文件(该文件虽然经常为空,但是缺失该文件,对模块的导入有很大的影响) ...

  2. c++学生成绩管理系统

    虽然比较水 =.= 但是写了两节课+一个中午 都是强迫症的锅 http://www.cnblogs.com/wenruo/p/4940182.html #include <cstdio> ...

  3. ios:Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?

    - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)laun ...

  4. UNITY 打包安卓APK

    1,安装JDK.这个直接下就行了. 2,安装android sdk相关.这个比较蛋疼,官网是被墙的.有些网站的包还是需要访问墙外下载的.关键是找对那个能用的包(对我来说就是不FQ). http://p ...

  5. 【转】Spring的WebServiceTemplate访问WebService的方法及其本质原理

    WebService客户端调用的本质就是将SAOP格式的XML通过通信协议发送到WebService的服务器端,然后接收服务器端返回的XML. 本文简单介绍一下如何通过Spring提供的WebServ ...

  6. 动作-CCActionInterval之CCGridAction家族

    CCGrid3DAction // 作用:x轴3D反转特效 CCActionInterval *flipX3D= CCFlipX3D::create(4); //    作用:Y轴3D反转特效 CCA ...

  7. 常用工具之stunnel

    The stunnel program is designed to work as an SSL encryption wrapper between remote client and local ...

  8. Cookie中用户登录信息的提示

    public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpSe ...

  9. 关于js原型的面试题

    今天遇到关于javascript原型的一道面试题,现分析下: 原题如下: function A(){ } function B(a){ this.a = a; } function C(a){ if( ...

  10. iOS 网络编程:XML解析

    1 XML文档结构 1.1 简介 XML 指可扩展标记语言(eXtensible Markup Language).XML 被设计用来传输和存储数据.其非常像HTML的标记语言,但与之不同的是,XML ...