模式匹配算法
1、基本概念:
目标串:s
模式串:t
模式串第 j 个元素 :t[j]
2、BF算法:
通过将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。
3、KMP算法:
通过利用已经部分匹配这个有效信息,保证 i 指针不回溯,通过修改 j 指针,让模式串尽量移动到有效位置。其中这个有效信息指的就是 next 数组和 nextval 数组。
3.1 next 数组手工求法
文字描述:
这里规定 next[0] = -1 ;
对于 next[j] ,考虑 j 之前的串,找出前缀和后缀相同的最长字串,设长度为 k ,则 next[j] = k 。
实例:
模式串 t : abcaba
比如求 next[5] ,考虑 abcab ,易知前缀和后缀相同的最长字串为 ab ,其长度为 2 ,故 next[5] = 2 。
3.2 nextval 数组手工求法
文字描述:
这里同样规定 nextval[0] = -1;
若 t[j] = t[next[j]] ,则 nextval[j] = nextval[next[j]] ,否则保持不变。
实例:
j | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
模式 t | a | b | c | a | b | a | a |
next[j] | -1 | 0 | 0 | 0 | 1 | 2 | 1 |
nextval[j] | -1 | 0 | 0 | -1 | 0 | 2 | 1 |
比如求 nextval[4] ,首先 t[4] = b = t[next[4]] = t[1] ,则 nextval[4] = nextval[next[4]] = nextval[1] = 0 ;
求 nextval[5] ,首先 t[5] = a =\= c = t[next[5]] = t[2] ,则 nextval[5] = next[5] = 2 。
4、完整代码及运行结果
苦逼的代码:
1 #include<stdio.h>
2 #include <string.h>
3 #define MaxSize 50
4 typedef char ElemType;
5
6 typedef struct {
7 ElemType data[MaxSize];
8 int length;
9 } SqString;
10
11 //BF 算法
12 int BFIndex(SqString s, SqString t) {
13 int i = 0, j = 0;
14 while(i < s.length && j < t.length) {
15 if (s.data[i] == t.data[j]) {
16 i++;
17 j++;
18 } else {
19 //目标串 s 指针 i 和模式串 t 指针 j 同步后移
20 i = i - j + 1;
21 j = 0;
22 }
23 }
24 if (j >= t.length) {
25 return i - t.length;
26 } else {
27 return -1;
28 }
29 }
30
31 //计算 next 数组
32 void GetNext(SqString t, int next[]) {
33 int j = 0, k = -1;
34 next[0] = -1;
35 while(j < t.length) {
36 if (k == -1 || t.data[j] == t.data[k]) {
37 j++;
38 k++;
39 next[j] = k;
40 } else {
41 k = next[k];
42 }
43 }
44 }
45 //依据 next 数组实现的 KMP 算法
46 int KMPIndex(SqString s, SqString t) {
47 int i = 0, j = 0;
48 int next[MaxSize];
49 GetNext(t, next);
50 while(i < s.length && j < t.length) {
51 if (j == -1 || s.data[i] == t.data[j]) {
52 i++;
53 j++;
54 } else {
55 //模式串 t 指针 j 后移
56 j = next[j];
57 }
58 }
59 if (j >= t.length) {
60 //返回模式串 t 所在位置物理下标
61 return i - t.length;
62 } else {
63 //匹配失败返回 -1
64 return -1;
65 }
66 }
67
68 //计算 nextval 数组
69 void GetNextval(SqString t, int nextval[]) {
70 int j = 0, k = -1;
71 nextval[0] = -1;
72 while(j < t.length) {
73 if (k == -1 || t.data[j] == t.data[k]) {
74 j++;
75 k++;
76 if (t.data[j] != t.data[k]) {
77 nextval[j] = k;
78 } else {
79 nextval[j] = nextval[k];
80 }
81 } else {
82 k = nextval[k];
83 }
84 }
85 }
86 //根据 nextval 数组实现的 KMP 算法
87 int KMPIndex1(SqString s, SqString t) {
88 int i = 0, j = 0;
89 int nextval[MaxSize];
90 GetNextval(t, nextval);
91 while(i < s.length && j < t.length) {
92 if (j == -1 || s.data[i] == t.data[j]) {
93 i++;
94 j++;
95 } else {
96 //模式串 t 指针 j 后移
97 j = nextval[j];
98 }
99 }
100 if (j >= t.length) {
101 //返回模式串 t 所在位置物理下标
102 return i - t.length;
103 } else {
104 //匹配失败返回 -1
105 return -1;
106 }
107 }
108
109 //将字符串数组赋值给 SqString 类型的字符串
110 void StrAssign(SqString &s, char csrt[]) {
111 int i;
112 for (i = 0; csrt[i] != '\0'; i++) {
113 s.data[i] = csrt[i];
114 }
115 s.length = i;
116 }
117
118 int main(int argc, char const *argv[]) {
119 SqString s, t;
120 StrAssign(s, (char *)"awzabcabaawanghizhi");
121 StrAssign(t, (char *)"abcabaa");
122
123 int next[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
124 int nextval[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
125 GetNext(t, next);
126 GetNextval(t, nextval);
127
128 printf("next and nextval:\n");
129 printf("j\t\t");
130 for (int i = 0; i < t.length; ++i) {
131 printf("%d\t", i);
132 }
133 printf("\nt[j]\t\t");
134 for (int i = 0; i < t.length; ++i) {
135 printf("%c\t", t.data[i]);
136 }
137 printf("\nnext[j]\t\t");
138 for (int i = 0; i < t.length; ++i) {
139 printf("%d\t", next[i]);
140 }
141 printf("\nnextval[j]\t");
142 for (int i = 0; i < t.length; ++i) {
143 printf("%d\t", nextval[i]);
144 }
145 printf("\n\n");
146
147 printf("BFIndex = %d\n", BFIndex(s, t));
148 printf("KMPIndex = %d\n", KMPIndex(s, t));
149 printf("KMPIndex1 = %d\n", KMPIndex1(s, t));
150 return 0;
151 }
运行结果:
next and nextval:
j 0 1 2 3 4 5 6
t[j] a b c a b a a
next[j] -1 0 0 0 1 2 1
nextval[j] -1 0 0 -1 0 2 1
BFIndex = 3
KMPIndex = 3
KMPIndex1 = 3
5、总结
花费了我两天中部分时间整理了模式匹配,只能算是初步掌握了这些,远达不到应用的地步。。。
之后的时间可能会整理一些应用样例。。。