字符串查找最简单的方法就是一个一个地“滑动”查找。这样查找算法复杂度可定很高,假设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;
}