字符串匹配是模式匹配最简单的一个问题,但在文本匹配领域字符串匹配是一个非常重要的一个主题。常见的字符串匹配算法有:Brute Force、KMP、Horspool、BM、Sunday以及RK算法等。
Brute Force算法:
暴力算法就是文本串每次移动向右移动一个字符,与模式串比较,如果每个字符都相等,则表示在文本串中找到与模式串匹配的子集。假设文本长度为N,模式串长度为M,则该算法最坏情况下要进行M*(N一M+1)次比较,时间复杂度为O(M*N)。
下面是暴力匹配算法的例子,其中第一参数为文本串,第二参数为模式串。
int BfMatch(char * p, char * t)
{
char * sp = p;
int lt = strlen(t);
int j = 0;
while(*sp++)
{
for(j = 0; j < lt; j++)
{
if(sp[j] != t[j])
break;
}
if(j == lt)
return sp - p;
}
return -1;
}
KMP算法:
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为KMP算法。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。其时间复杂度为O(M+N)
在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配。右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠。
下面是KMP算法的例子,其中第一参数为文本串,第二参数为模式串。
int KmpMatch(char* p, char* t)
{
char * sp = p;
int lt = strlen(t);
int *next = new int[lt];
int i, j, k;
for(i = 0; i < lt; i++) //计算next[]数组,即部分匹配表
{
for(j = 0; j < i; j++)
{
for(k = 0; k < j + 1; k++)
{
if(t[k] != t[i - j + k]) break;
}
if(k == j + 1)
next[i] = j + 1;
}
next[0] = -1;
}
while(*(sp+lt-1)) //进行KMP匹配
{
for(j = 0; j < lt; j++)
{
if(sp[j] != t[j])
break;
}
if(j == lt)
{
delete[] next;
return sp - p;
}
else
k = j - next[j];
sp = sp + k;
}
delete[] next;
return -1;
}
Horspool算法:
Horspool算法将主串中匹配窗口的最后一个字符跟模式串中的最后一个字符比较。如果相等,继续从后向前对主串和模式串进行比较,直到完全相等或者在某个字符处不匹配为止。如果不匹配,则根据主串匹配窗口中的最后一个字符在模式串中的下一个出现位置将窗口向右移动。
int HorspoolMatch(char *p, char * t)
{
char * sp = p;
int lt = strlen(t);
int i = 0, j = 0;
while(*sp)
{
for(i = lt; i > 0; i--)
{
if(sp[i-1] != t[i-1])
break;
}
if(i == 0)
return sp - p;
for(j = lt ; j > 0; j--)
{
if(sp[i-1] == t[j-1])
break;
}
if(j == 0)
sp = sp + i;
else if(j < i)
sp = sp + i - j;
else
sp++;
}
return -1;
}