• 字符串匹配算法(BF、KMP)


    目录

    1、暴力匹配(BF)算法

    2、KMP算法


    1、暴力匹配(BF)算法

    BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T 的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和 T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

    假定我们给出字符串 ”ababcabcdabcde”作为主串, 然后给出子串: ”abcd”,现在我们需要查找子串是否在主串中 出现,出现返回主串中的第一个匹配的下标,失败返回-1 ;

    只要在匹配的过程当中,匹配失败,那么 i回退到刚刚位置的下一个,j 回退到0下标重新开始。

     代码:

    1. #include
    2. #include
    3. #include
    4. //str 主串
    5. //sub 子串
    6. //如果找到了,返回子串在主串中的下标,否则,返回-1
    7. int BF(char* str, char* sub)
    8. {
    9. assert(str && sub);
    10. if (str == NULL && sub == NULL)
    11. return -1;
    12. int lenStr = strlen(str);
    13. int lenSub = strlen(sub);
    14. int i = 0;//主串开始的下标
    15. int j = 0;//子串开始的下标
    16. while (i < lenStr && j < lenSub)
    17. {
    18. if (str[i] == sub[j])
    19. {
    20. i++;
    21. j++;
    22. }
    23. else
    24. {
    25. i = i - j + 1;
    26. j = 0;
    27. }
    28. }
    29. //如果子串走到空,代表找到了
    30. if (j >= lenSub)
    31. {
    32. return i - j;
    33. }
    34. //如果主串走到空,证明主串里没有子串
    35. return -1;
    36. }
    37. int main()
    38. {
    39. printf("%d\n", BF("ababcabcdabcde", "abcd"));//5
    40. printf("%d\n", BF("ababcabcdabcde", "abcde"));//9
    41. printf("%d\n", BF("ababcabcdabcde", "abcdef"));//-1
    42. return 0;
    43. }

    2、KMP算法

    KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫 里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次 数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n) 。

    区别:KMP 和 BF 唯一不一样的地方在,我主串的 i 并不会回退,并且 j 也不会移动到 0 号位置 

    KMP 的精髓就是 next 数组:也就是用 next[j] = k;来表示,不同的 j 来对应一个 K 值, 这个 K 就是你将来要移动的 j 要移动的位置。 

    1、规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 字符开始,另一个以 j-1下标字符结尾。

    2、不管什么数据 next[0] = -1;next[1] = 0;在这里,我们以下标来开始,而说到的第几个第几个是从 1 开始(next[0]也可以是0,只需要在写代码的时候控制好就可以了)。

    求next数组:找到两个相等的字符串,第一个串必须以0下标的字母开头,第二个串必须以下标为j-1(j为目标字符)的字母结尾,这串相等的字符串的长度就是目标字母next数组的值。

    例1:对于”ababcabcdabcde”, 求其的 next 数组?

     例2:”abcabcabcabcdabcde”,求其的 next 数组?

     对此,我们对next数组是什么也有了一定的了解。根据观察,我们发现当p[i]==p[k]时(p为字符串),next数组会递增,那么如何证明呢?

    接下来的问题就是,已知next[i] = k;怎么求next[i+1] = ?

    如果我们能够通过 next[i]的值,通过一系列转换得到 next[i+1]得值,那么我们就能够实现这部分。

     代码:

    1. #include
    2. #include
    3. #include
    4. void GetNext(int* next, const char* sub)
    5. {
    6. int lensub = strlen(sub);
    7. next[0] = -1;
    8. next[1] = 0;
    9. int i = 2;//下一项
    10. int k = 0;//前一项的K
    11. while (i < lensub)//next数组还没有遍历完
    12. {
    13. if ((k == -1) || sub[k] == sub[i - 1])//
    14. {
    15. next[i] = k + 1;
    16. i++;
    17. k++;//k = k+1下一个K的值新的K值
    18. }
    19. else
    20. {
    21. k = next[k];
    22. }
    23. }
    24. }
    25. int KMP(const char* s, const char* sub, int pos)
    26. {
    27. int i = pos;
    28. int j = 0;
    29. int lens = strlen(s);
    30. int lensub = strlen(sub);
    31. int* next = (int*)malloc(lensub * sizeof(int));//和子串一样长
    32. assert(next != NULL);
    33. GetNext(next, sub);
    34. while (i < lens && j < lensub)
    35. {
    36. if ((j == -1) || (s[i] == sub[j]))
    37. {
    38. i++;
    39. j++;
    40. }
    41. else
    42. {
    43. j = next [j];
    44. }
    45. }
    46. free(next);
    47. if (j >= lensub)
    48. {
    49. return i - j;
    50. }
    51. else
    52. {
    53. return -1;
    54. }
    55. }
    56. int main()
    57. {
    58. char* str = "ababcabcdabcde";
    59. char* sub = "abcd";
    60. printf("%d\n", KMP(str, sub, 0));
    61. return 0;
    62. }

    next数组的优化 

    next 数组的优化,即如何得到 nextval 数组:有如下串: aaaaaaaab,他的 next 数组是-1,0,1,2,3,4,5,6,7. 而修正后的数组 nextval 是: -1, -1, -1, -1, -1, -1, -1, -1, 7。为什么出现修正后的数组,假设在 5 号处失败了,那退一步还是 a,还是相等,接着退还是 a。

     

    nextval数组的求法很简单,如果当前回退的位置,正好是和当前字符一样,那么就写那个字符的nextval值。不一样就写自己的。

    如上面例子中:4的下标字符a,应该回退到下标为1位置,下标为1位置不是a.所以,nextval值,就是当前next值。
    比如5的下标字符b,应该回退到下标为1位置,下标为1位置也是b.那么此时的nextval值,就是1下标b的nextval值。

    优化后得到next数组代码:

    1. void GetNext(int* next, const char* sub)
    2. {
    3. int lensub = strlen(sub);
    4. next[0] = -1;
    5. next[1] = 0;
    6. int i = 2;//下一项
    7. int k = 0;//前一项的K
    8. while (i < lensub)
    9. {
    10. if ((k == -1) || sub[k] == sub[i - 1])
    11. {
    12. if (sub[i] == sub[k])//当两个字符相同时,就跳过
    13. next[i] = next[k];
    14. else
    15. next[i] = k + 1;
    16. i++;
    17. k++;
    18. }
    19. else
    20. {
    21. k = next[k];
    22. }
    23. }
    24. }

  • 相关阅读:
    计算机的磁盘与中断介绍
    10.31+11.1
    在EXCEL中构建加载项之创建加载项的目的及规范要求
    律师事务所站
    SpringBoot 后端配置 Https 教程
    【Vue 开发实战】生态篇 # 18:Vuex最佳实践
    构建微波和毫米波自动测试系统需要考虑哪些因素?(一)
    入坑机器学习:二,监督学习
    .NET周刊【7月第3期 2023-07-16】
    Python015--python常用库之turtle库(简单的入门)
  • 原文地址:https://blog.csdn.net/m0_55752775/article/details/127628622