数据结构KMP算法配图详解

解决问题类型

KMP算法的作用是在一个已知字符串中查找子串的位置,也叫做串的模式匹配。 :::info 给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
输入:haystack = “hello”, needle = “ll” 输出:2 :::

操作流程

  • 假设现在文本串 S 匹配到 i 位置,模式串 P 匹配到 j 位置
  • 如果 j = -1,或者当前字符匹配成功(即 S[i] == P[j] ),都令 i++,j++,继续匹配下一个字符;
  • 如果 j != -1,且当前字符匹配失败(即 S[i] != P[j] ),则令 i 不变,j = next[j]。此举意味着失配时,模式串 P相对于文本串 S 向右移动了 j - next [j] 位
  • 换言之,将模式串 P 失配位置的 next 数组的值对应的模式串 P 的索引位置移动到失配处


算法内容概念

image.png

next数组

用一个next数组存储子串的最长相等前后缀的长度。而且next数组的数值只与子串本身有关。
所以next[i]=j,含义是:下标为i 的字符前的字符串最长相等前后缀的长度为j。 :::warning 子串t= “abcabcmn”的next数组为next[0]=-1(前面没有字符串单独处理)
next[1]=0;next[2]=0;next[3]=0;next[4]=1;next[5]=2;next[6]=3;next[7]=0; :::

“提示”示例代码

  1. public class Solution
  2. {
  3. public int StrStr(string haystack, string needle)
  4. {
  5. // 排除子串为空
  6. if (string.IsNullOrEmpty(needle))
  7. {
  8. return 0;
  9. }
  10. // 排除子串字长大于匹配字符串以及匹配字符串为空
  11. if (needle.Length > haystack.Length || string.IsNullOrEmpty(haystack))
  12. {
  13. return -1;
  14. }
  15. // 调用KMP算法
  16. return KMP( haystack, needle);
  17. }
  18. }
  19. private static int KMP(string haystack, string needle)
  20. {
  21. // 创建 next数组:最长匹配长度+1
  22. int[] next = GetNext(needle);
  23. // 字符串下标
  24. int i = 0;
  25. int j = 0;
  26. // KMP判断循环
  27. while (i < haystack.Length)
  28. {
  29. if (haystack[i] == needle[j]) // 字符相等,两者各进一步
  30. {
  31. j++;
  32. i++;
  33. }
  34. if (j == needle.Length)
  35. {
  36. return i - j;
  37. }
  38. else if (i < haystack.Length && haystack[i] != needle[j])
  39. {
  40. if (j != 0)
  41. j = next[j - 1];
  42. else
  43. i++;
  44. }
  45. }
  46. return -1;
  47. }
  48. // 计算获取next数组内的值
  49. private static int[] GetNext(string str)
  50. {
  51. int[] next = new int[str.Length];//数组内元素个数等于子串字符数
  52. next[0] = 0;
  53. int i = 1;// 指针初始位
  54. int j = 0;// 元素值
  55. while (i < str.Length)
  56. {
  57. if (str[i] == str[j])
  58. {
  59. j++;
  60. next[i] = j;
  61. i++;
  62. }
  63. else
  64. {
  65. if (j == 0)
  66. {
  67. next[i] = 0;
  68. i++;
  69. }
  70. // 回溯
  71. else
  72. {
  73. j = next[j - 1];
  74. }
  75. }
  76. }
  77. return next;
  78. }