• 模式匹配——从BF算法到KMP算法


     1.BF算法

    即暴力算法,是普通的模式匹配算法

    算法思想:

    将主串S的第一个字符与子串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符与T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。

    普通模式匹配算法,其实现过程没有任何技巧,就是简单粗暴地拿一个串同另一个串中的字符一一比对,得到最终结果。

    代码复杂度

    最理想的时间复杂度为O(n),即第一次匹配成功,n为子串的长度

    最坏情况的时间复杂度O(n*m),即两个串每次匹配,都必须匹配至子串的最末尾才能判断匹配失败,n为子串长度,m为主串长度。

    BF算法实现

    1. int BF(const char* str, const char* sub, int pos)//O(n*m)
    2. //pos指从主串中该位置开始进行比较
    3. {
    4. assert(str != NULL && sub != NULL);
    5. if (str == NULL || sub == NULL || pos<0 || pos>strlen(str))
    6. {
    7. return -1;
    8. }
    9. int i = pos;
    10. int j = 0;
    11. int lenstr = strlen(str);
    12. int lensub = strlen(sub);
    13. while (i < lenstr && j < lensub)
    14. {
    15. if (str[i] == sub[j])//相等,往后走
    16. {
    17. i++;
    18. j++;
    19. }
    20. else//不相等,i回退到刚才位置的下一个+1,j回退到0
    21. {
    22. i = i - j + 1;
    23. j = 0;
    24. }
    25. }
    26. //利用子串是否遍历完成,来判断查找是否成功,注意:不能利用主串来判断;
    27. if (j >= lensub)
    28. {
    29. return i - j;
    30. }
    31. else
    32. {
    33. return -1;
    34. }
    35. }
    36. int main()
    37. {
    38. const char* str1 = "ababcabcdabcde";
    39. const char* str2 = "abcd";
    40. printf("%d \n", BF(str1, str2, 6));//9
    41. printf("%d \n", BF(str1, str2, 10));//-1
    42. return 0;
    43. }

    2.KMP算法

    KMP算法不回退主串的指针i,借助next数组中储存的信息把子串移动到正确的位置继续进行匹配,时间复杂度为O(n+m),m为主串长度,n为子串长度

    理解next数组

    next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。

    也意味着在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)。如果next [j] 等于0或-1,则跳到模式串的开头字符,若next [j] = k 且 k > 0,代表下次匹配跳到j 之前的某个字符,而不是跳到开头,且具体跳过了k 个字符。

    求解next数组

    计算next数组,只与子串有关

    next数组的本质是寻找子串中“相同前后缀的长度”,并且一定是最长的前后缀

     

     首先对于第一个字符,显然不存在比它短的前后缀,所以next直接为0

     接着对于前两个字符,同样没有相同的前后缀,所以next为零

    对于前三个字符,由于A是公共的前后缀,所以next为1

    对于前四个字符,由于AB是共同的前后缀,next为2

    对于前五个字符,找不到相同的前后缀,next为0

    由此得到整个next数组。

    KMP算法实现

    1. static int* GetNext(const char* str)
    2. {
    3. int len = strlen(str);
    4. int* next = (int*)malloc(len * sizeof(int));
    5. next[0] = -1;//不能再退;
    6. next[1] = 0;//j=1,k=0;
    7. int j = 1;
    8. int k = 0;
    9. while (j+1 < len)
    10. {
    11. if ((k==-1)||str[k] == str[j])
    12. {
    13. next[++j] = ++k;
    14. /*next[j + 1] = k + 1;
    15. j = j + 1;
    16. k = k + 1;*///OK
    17. }
    18. else
    19. {
    20. k = next[k];
    21. }
    22. }
    23. return next;
    24. }
    25. int KMP(const char* str, const char* sub, int pos)//O(n*m)
    26. {
    27. assert(str != NULL && sub != NULL);
    28. if (str == NULL || sub == NULL || pos<0 || pos>strlen(str))
    29. {
    30. return -1;
    31. }
    32. int i = pos;
    33. int j = 0;
    34. int lenstr = strlen(str);
    35. int lensub = strlen(sub);
    36. int* next = GetNext(sub);
    37. while (i < lenstr && j < lensub)
    38. {
    39. if ((j==-1)||str[i] == sub[j])//相等,往后走
    40. {
    41. i++;
    42. j++;
    43. }
    44. else//不相等,i不回退,
    45. {
    46. //i = i - j + 1;//i不回退
    47. j = next[j];
    48. }
    49. }
    50. free(next);
    51. //利用子串是否遍历完成,来判断查找是否成功,注意:不能利用主串来判断;
    52. if (j >= lensub)
    53. {
    54. return i - j;
    55. }
    56. else
    57. {
    58. return -1;
    59. }
    60. }
    61. int main()
    62. {
    63. const char* str1 = "ababcabcdabcde";
    64. const char* str2 = "abcd";
    65. printf("%d \n", KMP(str1, str2, 0));//5
    66. return 0;
    67. }
  • 相关阅读:
    ES & Kibana 安装
    基础安全:CSRF攻击原理与防范
    【MySQL】BIT_OR函数在二进制分组group by中的妙用
    【二分查找】Leetcode 33. 搜索旋转排序数组【中等】
    MySQL Explain性能调优详解
    mathtype符号显示不全的问题
    抖音小店无货源,正处于红利期内的电商项目,新手能操作吗?
    图的应用2.0-----最短通路问题(Dijkstra和Floyd算法)
    某银行软件测试笔试题,满分一百你能得多少分?
    个人和企业如何做跨境电商?用API电商接口教你选平台选品决胜跨境电商
  • 原文地址:https://blog.csdn.net/weixin_59179454/article/details/124740727