字符串查找最简单的方法就是一个一个地“滑动”查找。这样查找算法复杂度可定很高,假设pattern的长度为M,文本txt的长度为N,那么算法复杂度为O(M(n-m-1))。
KMP模式搜索算法
KMP(Knuth morris pratt)我只认识Knuth,大名鼎鼎的高纳德老头子嘛。
KMP算法的基本思想是,当“失配”的时候,利用之前已经比较过的字串信息,确定重新开始比较时pattern串的开始的位置,而不是每次都要从头开始比较。
KMP算法先预处理一下pattern串pat[],构造出来一个lps[]数组,lps的意思是longest proper prefix which is also suffix(最长前缀也是最长后缀)。对于字串pat[0...i],i取值可以是0到m,m是pat的长度。lps[i]存储的值是子模式串pat[0...i]的最大前缀后缀的长度。
lps[i] = the longest proper prefix of pat[0..i] which is also a suffix of pat[0..i].
Examples:
For the pattern “AABAACAABAA”, lps[] is [0, 1, 0, 1, 2, 0, 1, 2, 3, 4, 5]
For the pattern “ABCDE”, lps[] is [0, 0, 0, 0, 0]
For the pattern “AAAAA”, lps[] is [0, 1, 2, 3, 4]
For the pattern “AAABAAA”, lps[] is [0, 1, 2, 0, 1, 2, 3]
For the pattern “AAACAAAAAC”, lps[] is [0, 1, 2, 0, 1, 2, 3, 3, 3, 4]
搜索算法:
不像简单搜索方法那样一个字符一个字符的滑动搜索,我们用lps[]确定pattern串滑动的位置。下面详细说明具体怎么比较:当pat[i]和txt[i]失配的时候,pat[0...j-1]和txt[i-j+1...i-1]是匹配的。如果滑动一位去再次比较,那么这pat[0...j-1]个字符肯定不匹配了。那么应该滑动多少位呢?这是就用到lps了,这是滑动lps[j-1]位.
预处理算法:
预处理就是用pat[]求出lsp[]数组。用变量len记录当前位置字串的最长前缀的长度。初始lps[0]=0,len=0.如果pat[len]和pat[i]匹配,长度len加1,然后将len赋值给lps[i].如果pat[i]和pat[len]不匹配,如果这是len不等于0,更新len为lps[len-1].参看下面代码了解更多细节。
#include <stdio.h> #include <stdlib.h> #include <string.h> // naive search void search(char* txt,char* pat) { int n=strlen(txt); int m=strlen(pat); for(int i=0;i<n-m;i++) { int j; for(j=0;j<m;j++) { if(txt[i+j]!=pat[j]) break; } if(j==m) printf("%d\n",i); } } // kmp search void computeLPSArray(char* pat,int m,int* lps); void KMPSearch(char* pat,char* txt) { int m=strlen(pat); int n=strlen(txt); int* lps=(int*)malloc(sizeof(int)*m); computeLPSArray(pat,m,lps); int i=0; int j=0; while(i<n) { if(txt[i]==pat[j]) { i++; j++; } if(j==m) { printf("found pattern at index %d\n",i-j); j=lps[j-1]; } else if(i<n && pat[j]!=txt[i]) { if(j!=0) j=lps[j-1]; else i++; } } free(lps); } // compute lps array void computeLPSArray(char* pat,int m,int* lps) { int len=0; int i=1; lps[0]=0; while(i<m) { if(pat[i]== pat[len]) { len++; lps[i]=len; i++; } else { if(len!=0) { len=lps[len-1]; } else { lps[i]=0; i++; } } } } int main(void) { char *txt = "AABAACAADAABAAABAA"; char *pat = "AABA"; search(txt,pat); printf("Hello World!\n"); KMPSearch(pat,txt); return 0; }