字符串匹配算法
字符串匹配算法有好多,朴素的,KMP,有限自动机,BM算法以及sunday算法,貌似还有个KR算法?
我只知道朴素的,KMP(刚刚学的),sunday算法(改进的BM算法)。。。
朴素字符串匹配算法:
1 int naive_match(char *str, char *patt) 2 { 3 if(str==NULL || patt==NULL) 4 return -1; 5 int i, j, 6 int str_len = strlen(str); 7 int patt_len = strlen(patt); 8 9 for(i=0; i<(str_len-patt_len); i++) 10 { 11 j = 0; 12 while(j<patt_len && patt[j]==str[i+j]) 13 j = j+1; 14 if(j==patt_len) 15 return i; 16 } 17 return -1; 18 }
KMP算法
这个算法。。。我只能说我太笨(智商永远是硬伤啊)。。。理解了好长时间,也好几次。。。
(推荐一个博客,这个人很厉害。。。http://www.matrix67.com/blog/,里面有一篇讲解KMP算法的。。。)
不好意思,matrix67,我就直接复制粘贴了
假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。我们看一看当 i=j=5时的情况。 i = 1 2 3 4 5 6 7 8 9 …… A = a b a b a b a a b a b … B = a b a b a c b j = 1 2 3 4 5 6 7 此时,A[6]<>B[6]。这表明,此时j不能等于5了,我们要把j改成比它小的值j'。j'可能是多少呢?仔细想一下,我们发现,j'必须要使得B[1..j]中的头j'个字母和末j'个字母完全相等(这样j变成了j'后才能继续保持i和j的性质)。这个j'当然要越大越好。在这里,B [1..5]="ababa",头3个字母和末3个字母都是"aba"。而当新的j为3时,A[6]恰好和B[4]相等。于是,i变成了6,而j则变成了 4: i = 1 2 3 4 5 6 7 8 9 …… A = a b a b a b a a b a b … B = a b a b a c b j = 1 2 3 4 5 6 7 从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。 再后来,A[7]=B[5],i和j又各增加1。这时,又出现了A[i+1]<>B[j+1]的情况: i = 1 2 3 4 5 6 7 8 9 …… A = a b a b a b a a b a b … B = a b a b a c b j = 1 2 3 4 5 6 7 由于P[5]=3,因此新的j=3: i = 1 2 3 4 5 6 7 8 9 …… A = a b a b a b a a b a b … B = a b a b a c b j = 1 2 3 4 5 6 7 这时,新的j=3仍然不能满足A[i+1]=B[j+1],此时我们再次减小j值,将j再次更新为P[3]: i = 1 2 3 4 5 6 7 8 9 …… A = a b a b a b a a b a b … B = a b a b a c b j = 1 2 3 4 5 6 7 现在,i还是7,j已经变成1了。而此时A[8]居然仍然不等于B[j+1]。这样,j必须减小到P[1],即0: i = 1 2 3 4 5 6 7 8 9 …… A = a b a b a b a a b a b … B = a b a b a c b j = 0 1 2 3 4 5 6 7 终于,A[8]=B[1],i变为8,j为1。事实上,有可能j到了0仍然不能满足A[i+1]=B[j+1](比如A[8]="d"时)。因此,准确的说法是,当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。
复制黏贴完毕!(我这么笨的都理解了(虽然代码不知道怎么写(代码能力很差的说!!!)),相信大家都能理解的!)
其实,KMP的预处理本身是一个模式串首尾“自我匹配”的过程!
代码:
get_next()
1 void get_next(char *patt, int next[]) 2 { 3 int i=0; 4 int j=-1; 5 if(patt[i]) 6 next[i] = j; 7 else 8 return; 9 while(i<strlen(patt)) 10 { 11 if(j==-1 || patt[i]==patt[j]) 12 { 13 ++i; 14 ++j; 15 if(patt[i]!=patt[j]) 16 { 17 next[i] = next[j]; 18 } 19 else 20 next[i] = j;
21 }
22 else
23 j = next[j];
24 }
25 }
kmp()
1 int kmp(char *str, char *patt) 2 { 3 int i,j; 4 i = 0; 5 j = 0; 6 int str_len = strlen(str); 7 int patt_len = strlen(patt); 8 int *next = new int[patt_len+1]; 9 get_next(patt, next); 10 while(i<str_len && j<patt_len) 11 { 12 if(j==-1 || str[i] == patt[j]) 13 { 14 ++i; 15 ++j; 16 } 17 else 18 j = next[j]; 19 } 20 if(j==patt_len) 21 return i-patt_len; 22 else 23 return -1; 24 }
Sunday算法(思想很。。。)
直接上代码吧?还是先复制一段从ACM代码库(貌似吉林大学的)中复制过来的分析吧。。。
例如我们要在"substring searching algorithm"查找"search",刚开始时,把子串与文本左边对齐: substring searching algorithm search 结果在第二个字符处发现不匹配,于是要把子串往后移动。但是该移动多少呢?这就是各种算法各显神通的地方了,最简单的做法是移动一个字符位置;KMP是利用已经匹配部分的信息来移动;BM算法是做反向比较,并根据已经匹配的部分来确定移动量。这里要介绍的方法是看紧跟在当前子串之后的那个字符(第一个字符串中的'i')。 显然,不管移动多少,这个字符是肯定要参加下一步的比较的,也就是说,如果下一步匹配到了,这个字符必须在子串内。所以,可以移动子串,使子串中的最右边的这个字符与它对齐。现在子串'search'中并不存在'i',则说明可以直接跳过一大片,从'i'之后的那个字符开始作下一步的比较,如下: substring searching algorithm search 比较的结果,第一个字符就不匹配,再看子串后面的那个字符,是'r',它在子串中出现在倒数第三位,于是把子串向后移动三位,使两个'r'对齐,如下: substring searching algorithm search 这次匹配成功了!
代码:
1 int sunday(char *str, char *patt) 2 { 3 int temp[256]; 4 int *shift = temp; 5 int str_len = strlen(str); 6 int patt_len = strlen(patt); 7 8 int i; 9 for(i=0; i<256; i++) 10 *(shift+i) = patt_len+1; 11 for(i=0; i<patt_len; i++) 12 *(shift+char(*(patt+i))) = patt_len - i; 13 int limit = str_len - patt_len +1; 14 for(i=0; i<limit; i+=shift[str[patt_len+i]]) 15 { 16 if(str[i]==*patt) 17 { 18 char *match_text = str+i+1; 19 int match_size = 1; 20 do{ 21 if(match_size==patt_size) 22 return i; 23 } while((*match_text++)==patt[match_size++]); 24 } 25 }
26 return -1; 27 }