一、BF算法:
定义:暴风(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。
代码如下:
int simple(const char *str1, const char *str2)//n*m
{
assert(NULL != str1 && NULL != str2);
int sit = 0;
int i = sit;
int j = 0;
while (str1[sit] != '\0' && str1[i] != '\0' && str2[j] != '\0')
{
i = sit;
j = 0;
while (str1[i] != '\0' && str2[j] != '\0' && str1[i] == str2[j])
{
i++;
j++;
} //若匹配相同则同时往后跑;
if (str2[j] == '\0')
{
return i - j; //模式串已经跑完,则说明匹配成功,返回匹配成功时子串的首元素的下标;
}
if (str1[i] == '\0')
{
return -1; // 目标串跑完,说明没有匹配成功;
}
sit++;
}
return -1;
}
举例说明:
S: ababcababa
T: ababa
BF算法匹配的步骤如下:
i=0, j=0 | i=1, j=1 | i=2,j=2 | i=3, j=3 | i=4, j=4(失败) |
ababcababa | ababcababa | ababcababa | ababcababa | ababcababa |
ababa | ababa | ababa | ababa | ababa |
i=1,j=0(失败) |
ababcababa |
ababa |
i=2,j=0 | i=3,j=1 | i=4,j=2(失败) |
ababcababa | ababcababa | ababcababa |
ababa | ababa | ababa |
i=3,j=0(失败) |
ababcababa |
ababa |
i=4,j=0(失败) |
ababcababa |
ababa |
i=5,j=0 | i=6,j=1 | i=7,j=2 | i=8,j=3 | i=9,j=4(成功) |
ababcababa | ababcababa | ababcababa | ababcababa | ababcababa |
ababa | ababa | ababa | ababa | ababa |
二、KMP算法:
定义:
KMP算法是一种改进的字符串匹配算法,KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。
主串T: a b a c a a b a c a b a c a b a a b b
模式串S:a b a c a b
在暴力字符串匹配过程中,我们会从T[0] 跟 W[0] 匹配,如果相等则匹配下一个字符,直到出现不相等的情况,此时我们会简单的丢弃前面的匹配信息,然后从T[1] 跟 W[0]匹配,循环进行,直到主串结束,或者出现匹配的情况。这种简单的丢弃前面的匹配信息,造成了极大的浪费和低下的匹配效率。然而,在KMP算法中,对于每一个模式串我们会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数。
比如,在简单的一次匹配失败后,我们会想将模式串尽量的右移和主串进行匹配。右移的距离在KMP算法中是如此计算的:在已经匹配的模式串子串中,找出最长的相同的前缀和后缀,然后移动使它们重叠。
在第一次匹配过程中
T: a b a c a a b a c a b a c a b a a b b
W: a b a c a b
在T[5]与W[5]出现了不匹配,而T[0]~T[4]是匹配的,现在T[0]~T[4]就是上文中说的已经匹配的模式串子串,现在移动找出最长的相同的前缀和后缀并使他们重叠:
T: a b a c a a b a c a b a c a b a a b b
W: a b a c a b
然后在从上次匹配失败的地方进行匹配,这样就减少了匹配次数,增加了效率。
具体实现代码如下:
void getNext(const char *str, int *next)
{
assert(NULL != str && NULL != next);
next[0] = -1;
int i = -1;
int j = 0;
while (j <= strlen(str))
{
if (i == -1 || str[i] == str[j])
{
i++;
j++;
next[j] = i;
}
else
{
i = next[i];
}
}
}
int KMP(const char *str1, const char *str2)
{
assert(NULL != str1 && NULL != str2);
int *next = (int*)malloc(sizeof(int)*strlen(str2));
assert(NULL != next);
getNext(str2, next);
int i = 0;
int j = 0;
while (str1[i] != '\0' && j < strlen(str2))
{
if (str1[i] == str2[j])
{
i++;
j++;
}
else
{
j = next[j];
}
}
if (j == strlen(str2))
{
return i - j;
}
return -1;
}
KMP算法是可以被进一步优化的。
比如给定P字符串是“abcdaabcab”,经过KMP算法,应当得到“特征向量”如下表所示:
下标i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
p(i) | a | b | c | d | a | a | b | c | a | b |
next[i] | -1 | 0 | 0 | 0 | 0 | 1 | 1 | 2 | 3 | 1 |
但是,如果此时发现p(i) == p(k),那么应当将相应的next[i]的值更改为next[k]的值。经过优化后可以得到下面的表格:
下标i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
p(i) | a | b | c | d | a | a | b | c | a | b |
next[i] | -1 | 0 | 0 | 0 | 0 | 1 | 1 | 2 | 3 | 1 |
优化的next[i] | -1 | 0 | 0 | 0 | -1 | 1 | 0 | 0 | 3 | 0 |