1 void NaiveStrMatch(char T[],char P[]) {
2 int i,j;
3 int len_t,len_p,len;
4 len_t = strlen(T);
5 len_p = strlen(P);
6 len = len_t - len_p;
7 for(i=0; i<len; i++) {
8 for(j=0; j<len_p; j++) {
9 if(T[i+j] != P[j]) break;
10 }
11 if(j == len_p) printf("the match position:%d-%d\n",i,(i+j-1));
12 }
13
14 }
1 void RabinKarpMatch(int T[],int P[], int q) {
2 //这里对整型进行举例,字符串的可以类推。
3 //q是我们取模时要用到的素数。
4 int len_t,len_p, len,i,j;
5 int pCount=0, tCount=0,h,temp;
6
7 len_t = sizeof(T)/sizeof(int);
8 len_p = sizeof(P)/sizeof(int);
9 len = len_t - len_p;
10 h = pow(10, len_p)%q;
11 for(i=0; i<len_p; i++) {
12 //预处理,计算出数组P对应的值,计算T第一个m位记录对应的值
13 pCount = (pCount * 10 + P[i])%q;
14 tCount = (tCount * 10 + T[i])%q;
15 }
16
17 for(i=0; i<len; i++) {
18 if(pCount == tCount) {
19 //值相等的时候,进行进一步的比较
20 temp = i+len_p;
21 for(j=i; j<temp; j++) {
22 if(T[j] != P[j-len_p]) break;
23 }
24 if(j == temp) printf("the match position:%d-%d\n",i,(i+len_p-1));
25 j = 0;
26 temp =0;
27 }
28 tCount = (10*(tCount - T[i]*h) + T[i+len_p])%q;
29 }
30
31 }
0 | 1 | 0 | 0 | a |
1 | 1 | 2 | 0 | b |
2 | 1 | 0 | 3 | c |
3 | 4 | 0 | 0 | a |
4 | 1 | 5 | 0 | b |
5 | 6 | 0 | 3 | a |
6 | 1 | 7 | 0 | b |
7 | 1 | 0 | 3 |
1 int FindMaxPreLen(char temp[], int len_temp, char P[], int len_p) {
2 //找出temp的后缀P的最长前缀的长度。len_temp 和len_p分别是两个数组的长度
3 int i,k=0;
4 char *arr1=NULL;
5 for(i=0; i<len_temp) {
6 if(i) {
7 if(temp[len_temp-1] == P[0]) k=1;
8 }else {
9 arr1 = &temp[len_temp-1-i];
10 if(!strncmp(arr1, P, (i+1))) k += 1;
11 }
12 }
13 return k;
14 }
15 void ComputeStateArray(char P[], char set[], int len, int StateArray[][]) {
16 //计算模式串P的自动机状态转变数组,set为记录集。 len为set的长度。
17 int i,j,len_p;
18 char *temp;
19
20 len_p = sizeof(P)/sizeof(char);
21 temp = (char*)malloc(sizeof(char) * (len_p));
22 if(!temp) {
23 printf("3、error in malloc function!\n");
24 exit(1);
25 }
26 for(i=0; i<=len_p; i++) {
27 strncpy(temp, P, i);
28 for(j=0; j<len; j++) {
29 temp[i] = set[P];
30 StateArray[i][j] = FindMaxPreLen(temp,i+1,P,len_p);
31 }
32 }
33 free(temp);
34 }
35 int StateChange(int curState, char curInput,int StateArray[][], char set[]) {
36 // StateArray[][] 状态的集合。
37 //该函数接收一个状态和一个输入字符,返回下一个状态
38 int i,j, len_s;
39 len_s = sizeof(set)/sizeof(char);
40 for(i=0; i<len_s; i++) {
41 if(set[i] == curInput) break;
42 }
43 if(i == len_s) {
44 printf("there is no such char %c",curInput);
45 exit(1);
46 }
47 return StateArray[curState][i];
48 }
49 int IsExist(char p[], char c, int len) {
50 // 检查P中是否存在字符c,是的时候返回1,否则返回0;
51 // len 为p中元素的个数
52 int i,flag =0;
53 for(i=0; i<len; i++) {
54 if(p[i] == c) {
55 flag=1;
56 break;
57 }
58 }
59 return flag;
60 }
61 void FiniteAutomationMatch(char T[], char P[]) {
62 int len_t,len_p,q,i,count=0;
63 int ** StateArray;
64 char *pt=NULL;
65 len_t = sizeof(T)/sizeof(char);
66 len_p = sizeof(P)/sizeof(char);
67 for(i=0; i<len_t; i++) {
68 if(i){
69 //这里假设了模式串P中出现的字符都包含在目标串T中
70 if(!IsExist(pt,T[i],count)) {
71 //不存在时要将它加入其中
72 pt = (char*)realloc(pt,sizeof(char)*(count+1));
73 if(!pt) {
74 printf("1、error in realloc function!\n");
75 exit(1);
76 }
77 pt[count] = T[i];
78 count++;
79 }
80 }else {
81 pt=(char*)malloc(sizeof(char));
82 if(!pt) {
83 printf("1、error in malloc function!\n");
84 exit(1);
85 }
86 pt[count] = T[i];
87 count++;
88 }
89 }
90 StateArray = (int**)malloc(sizeof(int*) * (len_p+1));
91 if(!StateArray) {
92 printf("2、error in malloc function!\n");
93 exit(1);
94 }
95 for(i=0; i<=len_p; i++) {
96 StateArray[i] = (int*)malloc( sizeof(int) * count );
97 if(!StateArray[i]) {
98 printf("3、error in malloc function!\n");
99 exit(1);
100 }
101 }
102 ComputeStateArray(P, pt, count, StateArray);
103 q=0; //q为初始状态
104
105 for(i=0; i<len_t; i++) {
106 q = StateChange(q,T[i],StateArray);
107 if(q == len_t) {
108 printf("the match position:%d-%d\n",i,(i+len_p-1));
109 }
110 }
111 free(pt);
112 free(StateArray);
113 }
1) 坏字符规则 P中的某个字符与T中的某个字符不相同时使用坏字符规则右移模式串P,P右移的距离可以通过delta1函数计算出来。delta1函数的定义如下: delta1(x) = 如果字符x在P中未出现,则为 m(模式长度); 否则 m-k, k是最大的整数使得P[k]=字符x,用数学表达式则为:m - max{k|P[k] = x, 1 <= k <= m};解释一下,如果字符x在模式P中没有出现,那么从字符x开始的m个文本显然不可能与P匹配成功,直接全部跳过该区域即可。如果x在模式P中出现,则以该字符进行对齐。
2) 好后缀规则 P中的某一子串P[j-s+1..m-s]与已比较部分P[j+1..m]相同,可让P右移s位。 delta2的定义如下:
核心思想:首先在主串T中查找模式串P的首字母,每找到一个则将它的位置存储,然后依次提取这些位置,从这些位置开始继续匹配模式串P。对于频繁使用的要匹配的主串和模式串来说,由于预先保存了模式串在主串中的所有存储位置,所以匹配速度会非常快。[不同于现有的字符串匹配算法,现有的匹配算法不是按照模式串从左到右就是从右到左进行直接比较的顺序匹配]
代码:
1 void FindFirstChar(char T[], int len, char Head,int *arr) {
2 //arr 中存放主串中,目标串首字符出现的次数。而arr[0]存放arr数组
3 //元素的个数
4 int i;
5 arr = (int*)malloc(sizeof(int));
6 if(!arr) {
7 printf("1、error in malloc function!");
8 exit(1);
9 }
10 arr[0]=0;
11 for(i=0; i<len; i++) {
12 if(T[i] == Head) {
13 arr[0]++;
14 arr = (int*)realloc(arr,sizeof(int)*(arr[0]+1));
15 arr[arr[0]] = i;
16 }
17 }
18
19 }
20
21 void ZZLMatch(char T[], char P[]) {
22 int i, j, len_t, len_p, len, *arr=NULL;
23 len_t = strlen(T);
24 len_p = strlen(P);
25 len = len_t - len_p;
26 FindFirstChar(T,len,P[0],arr);
27 for(i=1; i<=arr[0]; i++) {
28 for(j=0; j<len_p; j++) {
29 if( T[arr[i]+j] != P[j]) break;
30 }
31 if(j == len_p) printf("the match position:%d-%d\n",i,(i+j-1)
32 }
33 free(arr);
34 }
时间复杂度: 预处理时间 O(n-m) + 匹配时间 O(km) 其中k为模式串P的首字符在目标串中出现的次数。
空间复杂度:O(n+m+k+6)
ZZL缺点分析
观察ZZL算法,如果模式串首字母在主串中出现次数很多,那么ZZL算法记录的匹配点将会增加,时间复杂度也随之增加。让我们先来考察下面的匹配场景:
S = tom orrow, m any and m any m anufactories will close.
T = manufactories
那么ZZL算法在预处理S时会找到4个开始匹配点,然而事实上,只有在最后一个匹配点上T和S才匹配成功。这样仍可以进行改进。
1 void FindFirstChar(char T[], int len, char Head,int short, int *arr) {
2 //arr 中存放主串中,目标串首字符出现的次数。而arr[0]存放arr数组
3 //元素的个数
4 int i;
5 arr = (int*)malloc(sizeof(int));
6 if(!arr) {
7 printf("1、error in malloc function!");
8 exit(1);
9 }
10 arr[0]=0;
11 for(i=0; i<len; i++) {
12 if(T[i] == Head && (i-arr[arr[0]] > short)) {
13 arr[0]++;
14 arr = (int*)realloc(arr,sizeof(int)*(arr[0]+1));
15 arr[arr[0]] = i;
16 }else {
17 arr[arr[0]] = i;
18 }
19 }
20
21 }
22
23 void ImproveZZLMatch(char T[], char P[]) {
24 int i, j, len_t, len_p, len, *arr=NULL, short;
25 len_t = strlen(T);
26 len_p = strlen(P);
27 len = len_t - len_p;
28 for(i=1; i<len_p; i++) {
29 if(P[i] == P[0]) {
30 short = i;
31 break;
32 }
33 }
34 FindFirstChar(T,len,P[0],short,arr);
35 for(i=1; i<=arr[0]; i++) {
36 for(j=0; j<len_p; j++) {
37 if( T[arr[i]+j] != P[j]) break;
38 }
39 if(j == len_p) printf("the match position:%d-%d\n",i,(i+j-1)
40 }
41 free(arr);
42 }
假设在发生不匹配时S[i]≠T[j],1≤i≤N,1≤j≤M。此时还未匹配的部分为u,并假设字符串u的长度为L。如图1。明显的,S[L+i+1]肯定要参加下一轮的匹配,并且T[M]至少要移动到这个位置(即模式串T至少向右移动一个字符的位置)。
图1 Sunday算法不匹配的情况
分如下两种情况:
(1) S[L+i+1]在模式串T中没有出现。这个时候模式串T[0]移动到S[L+i+1]之后的字符的位置。如图2。
图2 Sunday算法移动的第1种情况
(2)S[L+i+1]在模式串T中出现。这里S[L+i+1]从模式串T的右侧,即按T[M-1]、T[M-2]、…T[0]的次序查找。如果发现S[L+i+1]和T中的某个字符相同,则记下这个位置,记为k,1≤k≤M,且T[k]=S[L+i+1]。此时,应该把模式串T向右移动M-k个字符的位置,即移动到T[k]和S[L+i+1]对齐的位置。如图3。
图3 Sunday算法移动的第2种情况
依次类推,如果完全匹配了,则匹配成功;否则,再进行下一轮的移动,直到主串S的最右端结束。对于短模式串的匹配问题,该算法执行速度较快。
代码
1 void sunday(const char *T,const char *P)
2 {
3 int i,j,pos=0;
4 int len_t,len_p;
5 int next[26]={0};
6
7 len_t=strlen(T);
8 len_p=strlen(P);
9
10 for(j=0; j<26; ++j)
11 next[j] = len_p;
12
13 for(j=0; j<len_p; ++j)
14 next[P[j]-'a']=len_p-j;
15
16 while( pos < (len_t-len_p+1) )
17 {
18 i=pos;
19 for(j=0; j<len_p; ++j,++i)
20 {
21 if(T[i]!=P[j])
22 {
23 pos+=next[T[pos+len_p]-'a'];
24 break;
25 }
26 }
27 if(j==len_d)
28 printf("the match position:%d-%d\n",pos,(pos+j-1));
29 }
30 }
举个例子
假设 T = “abaabcabca” len_t = 10
P = “abcabc” len_p = 6
一开始的时候next[0,1,2,…,25] = {6} 经过13行的for循环后变成了next[0,1,2] = {3,2,1} next[3,……25] ={6};由于我们的模式串P中只出现了字符a,b,c他们对应的位置分别为0,1,2; 而所以存储在这三个位置。
在开始比较时,pos=0, 在i=j=2时不匹配,此时要进行pos位置的移动,那么再怎么样移动都至少要移一个位置,那么下一个位置也就是T中第7个元素a在下一个匹配过程中是一定要比较的,而a对应的next为3,将pos移动到3的位置去,再接着一轮匹配。next数组中存放的就是从右往左数某字符第一次出现的位置。
该算法最坏情况下的时间复杂度为O(N*M)