字符串匹配问题-------BF算法和KMP算法

一、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

你可能感兴趣的:(c)