模式串匹配、KMP算法及其改进(代码)

  1. #include "string.h"
  2. #include "stdio.h"
  3. #include "stdlib.h"
  4. #include "io.h"
  5. #include "math.h"
  6. #include "time.h"
  7. #define OK 1
  8. #define ERROR 0
  9. #define TRUE 1
  10. #define FALSE 0
  11. #define MAXSIZE 100 /* 存储空间初始分配量 */
  12. typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
  13. typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
  14. typedef char String[MAXSIZE + 1]; /* 0号单元存放串的长度 */
  15. /* 生成一个其值等于chars的串T */
  16. Status StrAssign(String T, char *chars)
  17. {
  18. int i;
  19. if (strlen(chars)>MAXSIZE)
  20. return ERROR;
  21. else
  22. {
  23. T[0] = strlen(chars);
  24. for (i = 1; i <= T[0]; i++)
  25. T[i] = *(chars + i - 1);
  26. return OK;
  27. }
  28. }
  29. Status ClearString(String S)
  30. {
  31. S[0] = 0;/* 令串长为零 */
  32. return OK;
  33. }
  34. /* 输出字符串T。 */
  35. void StrPrint(String T)
  36. {
  37. int i;
  38. for (i = 1; i <= T[0]; i++)
  39. printf("%c", T[i]);
  40. printf("\n");
  41. }
  42. /* 输出Next数组值。 */
  43. void NextPrint(int next[], int length)
  44. {
  45. int i;
  46. for (i = 1; i <= length; i++)
  47. printf("%d", next[i]);
  48. printf("\n");
  49. }
  50. /* 返回串的元素个数 */
  51. int StrLength(String S)
  52. {
  53. return S[0];
  54. }
  55. /* 朴素的模式匹配法 */
  56. int Index(String S, String T, int pos)
  57. {
  58. int i = pos; /* i用于主串S中当前位置下标值,若pos不为1,则从pos位置开始匹配 */
  59. int j = 1; /* j用于子串T中当前位置下标值 */
  60. while (i <= S[0] && j <= T[0]) /* 若i小于S的长度并且j小于T的长度时,循环继续 */
  61. {
  62. if (S[i] == T[j]) /* 两字母相等则继续 */
  63. {
  64. ++i;
  65. ++j;
  66. }
  67. else /* 指针后退重新开始匹配 */
  68. {
  69. i = i - j + 2; /* i退回到上次匹配首位的下一位 */
  70. j = 1; /* j退回到子串T的首位 */
  71. }
  72. }
  73. if (j > T[0])
  74. return i - T[0];
  75. else
  76. return 0;
  77. }
  78. /* 通过计算返回子串T的next数组。 */
  79. void get_next(String T, int *next)
  80. {
  81. int i, j;
  82. i = 1;
  83. j = 0;
  84. next[1] = 0;
  85. while (i<T[0]) /* 此处T[0]表示串T的长度 */
  86. {
  87. if (j == 0 || T[i] == T[j]) /* T[i]表示后缀的单个字符,T[j]表示前缀的单个字符 */
  88. {
  89. ++i;
  90. ++j;
  91. next[i] = j;
  92. }
  93. else
  94. j = next[j]; /* 若字符不相同,则j值回溯 */
  95. }
  96. }
  97. /* 返回子串T在主串S中第pos个字符之后的位置。若不存在,则函数返回值为0。 */
  98. /* T非空,1≤pos≤StrLength(S)。 */
  99. int Index_KMP(String S, String T, int pos)
  100. {
  101. int i = pos; /* i用于主串S中当前位置下标值,若pos不为1,则从pos位置开始匹配 */
  102. int j = 1; /* j用于子串T中当前位置下标值 */
  103. int next[255]; /* 定义一next数组 */
  104. get_next(T, next); /* 对串T作分析,得到next数组 */
  105. while (i <= S[0] && j <= T[0]) /* 若i小于S的长度并且j小于T的长度时,循环继续 */
  106. {
  107. if (j == 0 || S[i] == T[j]) /* 两字母相等则继续,与朴素算法增加了j=0判断 */
  108. {
  109. ++i;
  110. ++j;
  111. }
  112. else /* 指针后退重新开始匹配 */
  113. j = next[j];/* j退回合适的位置,i值不变 */
  114. }
  115. if (j > T[0])
  116. return i - T[0];
  117. else
  118. return 0;
  119. }
  120. /* 求模式串T的next函数修正值并存入数组nextval */
  121. void get_nextval(String T, int *nextval)
  122. {
  123. int i, j;
  124. i = 1;
  125. j = 0;
  126. nextval[1] = 0;
  127. while (i<T[0]) /* 此处T[0]表示串T的长度 */
  128. {
  129. if (j == 0 || T[i] == T[j]) /* T[i]表示后缀的单个字符,T[j]表示前缀的单个字符 */
  130. {
  131. ++i;
  132. ++j;
  133. if (T[i] != T[j]) /* 若当前字符与前缀字符不同 */
  134. nextval[i] = j; /* 则当前的j为nextval在i位置的值 */
  135. else
  136. nextval[i] = nextval[j]; /* 如果与前缀字符相同,则将前缀字符的 */
  137. /* nextval值赋值给nextval在i位置的值 */
  138. }
  139. else
  140. j = nextval[j]; /* 若字符不相同,则j值回溯 */
  141. }
  142. }
  143. int Index_KMP1(String S, String T, int pos)
  144. {
  145. int i = pos; /* i用于主串S中当前位置下标值,若pos不为1,则从pos位置开始匹配 */
  146. int j = 1; /* j用于子串T中当前位置下标值 */
  147. int next[255]; /* 定义一next数组 */
  148. get_nextval(T, next); /* 对串T作分析,得到next数组 */
  149. while (i <= S[0] && j <= T[0]) /* 若i小于S的长度并且j小于T的长度时,循环继续 */
  150. {
  151. if (j == 0 || S[i] == T[j]) /* 两字母相等则继续,与朴素算法增加了j=0判断 */
  152. {
  153. ++i;
  154. ++j;
  155. }
  156. else /* 指针后退重新开始匹配 */
  157. j = next[j];/* j退回合适的位置,i值不变 */
  158. }
  159. if (j > T[0])
  160. return i - T[0];
  161. else
  162. return 0;
  163. }
  164. int main()
  165. {
  166. int i, *p;
  167. String s1, s2;
  168. StrAssign(s1, "abcdex");
  169. printf("子串为: ");
  170. StrPrint(s1);
  171. i = StrLength(s1);
  172. p = (int*)malloc((i + 1)*sizeof(int));
  173. get_next(s1, p);
  174. printf("Next为: ");
  175. NextPrint(p, StrLength(s1));
  176. printf("\n");
  177. StrAssign(s1, "abcabx");
  178. printf("子串为: ");
  179. StrPrint(s1);
  180. i = StrLength(s1);
  181. p = (int*)malloc((i + 1)*sizeof(int));
  182. get_next(s1, p);
  183. printf("Next为: ");
  184. NextPrint(p, StrLength(s1));
  185. printf("\n");
  186. StrAssign(s1, "ababaaaba");
  187. printf("子串为: ");
  188. StrPrint(s1);
  189. i = StrLength(s1);
  190. p = (int*)malloc((i + 1)*sizeof(int));
  191. get_next(s1, p);
  192. printf("Next为: ");
  193. NextPrint(p, StrLength(s1));
  194. printf("\n");
  195. StrAssign(s1, "aaaaaaaab");
  196. printf("子串为: ");
  197. StrPrint(s1);
  198. i = StrLength(s1);
  199. p = (int*)malloc((i + 1)*sizeof(int));
  200. get_next(s1, p);
  201. printf("Next为: ");
  202. NextPrint(p, StrLength(s1));
  203. printf("\n");
  204. StrAssign(s1, "ababaaaba");
  205. printf(" 子串为: ");
  206. StrPrint(s1);
  207. i = StrLength(s1);
  208. p = (int*)malloc((i + 1)*sizeof(int));
  209. get_next(s1, p);
  210. printf(" Next为: ");
  211. NextPrint(p, StrLength(s1));
  212. get_nextval(s1, p);
  213. printf("NextVal为: ");
  214. NextPrint(p, StrLength(s1));
  215. printf("\n");
  216. StrAssign(s1, "aaaaaaaab");
  217. printf(" 子串为: ");
  218. StrPrint(s1);
  219. i = StrLength(s1);
  220. p = (int*)malloc((i + 1)*sizeof(int));
  221. get_next(s1, p);
  222. printf(" Next为: ");
  223. NextPrint(p, StrLength(s1));
  224. get_nextval(s1, p);
  225. printf("NextVal为: ");
  226. NextPrint(p, StrLength(s1));
  227. printf("\n");
  228. StrAssign(s1, "00000000000000000000000000000000000000000000000001");
  229. printf("主串为: ");
  230. StrPrint(s1);
  231. StrAssign(s2, "0000000001");
  232. printf("子串为: ");
  233. StrPrint(s2);
  234. printf("\n");
  235. printf("主串和子串在第%d个字符处首次匹配(朴素模式匹配算法)\n", Index(s1, s2, 1));
  236. printf("主串和子串在第%d个字符处首次匹配(KMP算法) \n", Index_KMP(s1, s2, 1));
  237. printf("主串和子串在第%d个字符处首次匹配(KMP改良算法) \n", Index_KMP1(s1, s2, 1));
  238. return 0;
  239. }

来自为知笔记(Wiz)

时间: 2024-10-31 13:01:56

模式串匹配、KMP算法及其改进(代码)的相关文章

模式串匹配--KMP算法

  前几天百度LBS部门实习二面,让写一个字符串匹配函数,当时忘记KMP怎么写了,就默默的写了一个暴力搜索,连尝试推导一下KMP都没有,结果自然是没有过,以后面试要多和面试官交流,就算忘记了,也要让他知道你试图推导,要不然他会觉得你可能都没有听过. KMP是对前缀暴力搜索的改进,基于的想法其实是很朴素的.首先我们来看一下暴力搜索. char* BF(char *src, char *pattern){ if(src == NULL || pattern == NULL) return NULL;

KMP算法及其改进

KMP算法及其改进 字符串匹配算法也就是从一个很长的字符串里面找出与我们手中的字符串相匹配的字符串(是这个大字符串的第几个字符开始),对于这个问题我们有很简单的解法,叫BF算法,Brute Force也就是蛮力的意思,充分依靠计算能力来解决问题的方法,对于这种解法可以用下面的图片来表述: 上面的算法就是BF算法,不好之处是效率太低了,因为就像第三趟比较中那样,我们只有最后一个元素没有匹配上就要从头再来,主串的对应元素竟然要回头从第四个元素开始比较,我们明明比较到了主串的第七个元素,前面的工作全部

字符串匹配KMP算法C++代码实现

看到了一篇关于<字符串匹配的KMP算法>(见下文)的介绍,地址:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html,这篇博客对KMP算法的解释很清晰,但缺点是没有代码的实现.所以本人根据这位大神的思路写了一下算法的C++实现. C++代码如下: #include <iostream> #include<string.h> using namesp

字符串匹配 - KMP算法

首先大致的学习一下有限自动机字符匹配算法,然后在讨论KMP算法. 有限自动机 一个有限自动机M是一个五元组(Q,q0,A,Σ,δ),其中: Q是状态的集合, q0∈Q是初始状态, A是Q的字集,是一个接受状态集合, Σ是一个有限的输入字母表, δ是一个从Q×Σ到Q的函数,叫做转移函数. 下面定义几个相关函数: φ(w)是M在扫描字符串w后终止时的状态.函数φ有下列递归关系定义:φ(ε) = q0,φ(wa) = δ(φ(w),a), σ(x)是x的后缀中,关于P的最长前缀的长度. 字符串匹配自动

字符串匹配KMP算法的理解(详细)

1. 引言 本KMP原文最初写于2年多前的2011年12月,因当时初次接触KMP,思路混乱导致写也写得混乱.所以一直想找机会重新写下KMP,但苦于一直以来对KMP的理解始终不够,故才迟迟没有修改本文. 然近期因开了个算法班,班上专门讲解数据结构.面试.算法,才再次仔细回顾了这个KMP,在综合了一些网友的理解.以及算法班的两位讲师朋友曹博.邹博的理解之后,写了9张PPT,发在微博上.随后,一不做二不休,索性将PPT上的内容整理到了本文之中(后来文章越写越完整,所含内容早已不再是九张PPT 那样简单

Java数据结构之字符串模式匹配算法---KMP算法2

直接接上篇上代码: 1 //KMP算法 2 public class KMP { 3 4 // 获取next数组的方法,根据给定的字符串求 5 public static int[] getNext(String sub) { 6 7 int j = 1, k = 0; 8 int[] next = new int[sub.length()]; 9 next[0] = -1; // 这个是规定 10 next[1] = 0; // 这个也是规定 11 // 12 while (j < sub.l

字符串匹配--kmp算法原理整理

kmp算法原理:求出P0···Pi的最大相同前后缀长度k: 字符串匹配是计算机的基本任务之一.举例,字符串"BBC ABCDAB ABCDABCDABDE",里面是否包含另一个字符串"ABCDABD"? 许多算法可以完成这个任务,Knuth-Morris-Pratt算法(简称KMP)是最常用的之一. KMP算法搜索如下: 1.首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的

AC自动机——多模式串匹配的算法思想

标准KMP算法用于单一模式串的匹配,即在母串中寻求一个模式串的匹配,但是现在又存在这样的一个问题,如果同时给出多个模式串,要求找到这一系列模式串在母串存在的匹配个数,我们应该如何处理呢? 基于KMP算法,我们能够想到的一个朴素算法就是,枚举这多个模式串,然后进行多次KMP算法,这个过程中完成计数,假设这里有n个模式串,那么整个算法的复杂度大约是O(n*m),m是母串的长度,这里的时间复杂度是粗略估计,没有计算辅助数组的时间(KMP中的next数组),但是这种复杂度还是太高,没有做到KMP算法中“

【数据结构与算法】字符串匹配KMP算法

首先需要了解一下BF暴力匹配算法,这个算法为每一个串设置一个指针,然后两个指针同时后移,出现不匹配的情况后,主串指针回到开始后移之前的位置的下一位,模式串指针回到最开始. 对比一下KMP算法,同样是设置两个指针,然后两个指针同时后移,出现不匹配的情况后,主串指针不变,模式串指针回溯一定的距离.具体模式串指针回溯多少,是第一次看KMP算法的人比较难以理解的,其实仔细想想,模式串的前缀和后缀其实也是在做匹配,当P[K]!=P[J]时就是失配,那么前缀的指针就需要回溯,所以后k=next[k]. 代码