模式匹配---KMP算法

如有错误,敬请指出,谢谢~

1.概念介绍

                                前缀

                                后缀

                                公共前后缀

                                最长公共前后缀

2.next数组

                               详细解说

3.模式匹配

                               图演示

4.代码

 

 

 

1.概念介绍

给出一段主字符串S,一段模式串M,从S中找出与M匹配的字符串。

前缀、后缀:除字符串本身

前缀:从第一个字符开始,依次向后所组成的所有子符串(除掉最后一个字符)

后缀:以最后一个字符为结尾,依次向前组成的所有子字符串(除掉第一个字符)

公共前后缀:前缀和后缀一致

最长公共前后缀:公共前后缀中长度最长的字符串。

举例:对给定的字符串 : a b a a b b a

前缀有哪些: a 、ab 、aba、 abaa 、abaab 、abaaba

后缀有哪些: a 、ba 、bba 、 abaa 、aabba 、 baabba

2.next数组

next数组是针对模式串而言的

next数组含义: j+1位失配,j应该回到的位置。

模式串M:a b a a b b a b a a b

        I字符前的字符的 最大公共前后缀长度

I = 0       首字符前面没有字符我们规定为 next[0] = -1        next数组

I = 1        a                                               0                          next[1] = 0

I = 2        ab                                             0                          next[2] = 0

I = 3       aba                                            1                          next[3] = 1

I = 4       abaa                                          1                          next[4] = 1

I = 5        abaab                                       1                          next[5] = 1

I = 6       abaaba                                      2                          next[6] = 2

I = 7       abaabab                                    3                          next[7] = 3

I = 8       abaababa                                  2                          next[8] = 2

I = 9       abaababaa                                3                          next[9] = 3

I = 10     abaababaab                              4                          next[10] = 4

aba最大公共前后缀长度为1,最大公共前后缀是a

abaa最大公共前后缀怎么找?

比上个子串aba新添加了一个字符a,将新添加的字符a与之前最大公共前后缀中的前缀a的下一个字符b比较,不一样,最大公共前后缀依然是a,长度还是1

abaab最大公共前后缀怎么找?

比上个字符串abaa新添加了一个字符b,将新添加的字符b与之前最大公共前后缀中的前缀a后一个字符b比较,一样,最大公共前后缀是ab,长度再加一,变成2.

下面开始写next数组,首先规定next数组第一位填-1,第二位填0

下标i

0

1

2

3

4

5

6

7

8

9

10

模式串

a

b

a

a

b

b

a

b

a

a

b

Next[i]

-1

0

0

1

1

1

2

3

2

3

4

了解了next数组是怎样算出来的之后,那么来思考一下,怎样再用计算机的思维把这些算出来:
给定字符:a b a a b b b a

第一位字符规定 -1 ,

第二位字符规定0,因为前缀后缀不能是自身,第二位之前就一个字符,1个字符无前缀后缀,更无最大公共前后缀,所以当然是 0

对于next数组。我们只看每个字符串除最后一个字符外的最长公共前后缀,指定j为最长公共前后缀前缀的游标+1,j所指的是最长公共前后缀的下一个字符,若无前后缀,则j=-1,最长公共前后缀长度就为 j+1 = 0,

当 i = 0, next[0] = -1

当 i = 1,串为a b ,只看一号位之前的字符串a,j指向a,j = 0, next[1] = j = 0; b与a不一样,无公共前后缀, j= next[0] = -1 ,

下面将j看成一个学武功的初学者,要去比试武力,增加等级(寻找最大公共前后缀长度),i的等级是不断增加的,因为i一直在学武功,j有时也会偷个懒,没学武功就想和i比试(m[i] !=m[j],不匹配情况)如果m[i] == m[j],表明j 今天学武功的与i今天学武功一样,j没偷懒。

当 i = 2,a b a,二号位前面的字符串为a b , j指向a , j = 0, a与 b不一样,next[2] = j = 0,

比试一下,m[2] == m[0], i++, j++;二号位与0号位字符相同,升级,i++,j++, i = 3,标记等级(a b a a,三号位之前的字符串为 a b a , 由上面知,m[2] = m[0],找到一个相同的前后缀),next[3] = j =1;

接着往下找, m[3] != m[1],(b != a),叮当,不相等了,那之前的有相等的吗?于是就去找1号位之前的字符串 a,它的公共前后缀情况,,j = next[1] = 0,只有一个a,无最长公共前后缀,那三号位就和a比较试试,m[3] == m[0] ,一样,j很高兴,松了一口气,幸好这个招式之前学过,不然就等着被贬为凡胎,于是i++,j++,准备比较下一位,但先把等级升了再说,next[4] = j = 1;

之后又开始比试了,m[4] == m[1] (b == b),一样,兴奋,兴奋就加起来,i++,j++,升等级,next[5] = j = 2

什么?有人不服,那就比试一场,m[5] != m[2] (b!=a)。果然,功力不够格,等着降级,i = 5,现在字符串为 a b a a b b ,j现在在第二级,第二级之前的字符有匹配的吗,来瞅瞅,next[2] = 0, 0呀,没有神仙/靠山(二号位之前无公共前后缀)来救j了,j的武力是虚假的,被发现了,又无靠山,犯欺君大罪,直接打回原形,j = next[j] = 0。J 不服,又回来比试了,m[5] != m[0](b!=a),再降级,j  = next[0] = -1

J = -1了,从凡胎开始修道,初生牛犊不怕虎,新的开始,当然来场比试,才够刺激。就凭这勇气,给j升级,j++,而i的功力在日益增加,i++,因为j刚开始无匹配,next[6] = j = 0

开始比试,m[6] == m[0](a == a),匹配上了,兴奋,升级i++,j++,标记等级,next[7] = j = 1,

来劲了,继续比试,m[7] == m[1](b== b),成功匹配,兴奋,升级,i++,j++,快标记,next[8] =j = 2,

信心上来了,快来比试,m[8] == m[2],真棒,匹配成功,升级i++,j++,记功,next[9] = j = 3

胜利要来了,加油,继续比试,m[9] == m[3],(b==b)升级,i++,j++,next[10] = 4

bravo!

 

指向最大公共前后缀 前缀的下一个更好的理解(next[4]的计算遇到此情况):
给定字符串: a a b s a a  t  a a b s a a  k  a a b s a a  t  a a b s a a   ax

计算x的next值,已知a的next值为13,表明a前面有长度为13最大公共前后缀。看x之前的子串最后一个字符是 a ,找 a 之前最大公共前后缀前缀的下一个值是k,字符k对应的下标就是13,这就是最大公共前后缀 前缀的下一个,k与a不一样,再找 k前子串中最大公共前后缀 前缀的下一个 t ,与 a 不一样,再往前找t前子串中最大公共前后缀 前缀的下一个字符 b,与 a不一样,再找b 之前的子串 aa中对应最大公共前后缀 前缀的下一个字符 a,与a一样,x的next值是2

(前缀后缀都是 a,到这又怎么看?

比如一串字符:a a a a a

Next值规定第一位、第二位字符分别是 -1 0,I = 2时,第三位字符 a的next值为 0 前面的字符串为 a a,最长公共前后缀为a,长度为1,注意前缀、后缀都是除了自身。)                                                                                                                                                                                                                                                                              

 

得到了next数组,有什么用呢?

3.模式匹配

给出主串S:A B A A B A A B B A B A A A B A A B B A B A A B

模式串M:   A B A A B B A B A A B

M遇到与S不匹配的字符时,就去找前面的字符串,找它们最大公共前后缀,找到之后直接将前缀移到后缀再进行比较,因为中间不会有与M匹配的字符串

 

理解这些,最后就是代码了

4.代码

//KMP
#include<iostream>
using namespace std;

//m:模式串;求next数组 
void getNext(string m,int* next)
{
	int i = 0;
	int j = -1;
	next[0] = -1;
	while (i<m.length()-1)
	{
		if (j == -1 || m[i] == m[j])
		{
			i++;
			j++;
			next[i] = j;
		}
		else
		{
			j = next[j];//找最长公共前后缀前缀的下一个 
		}
	}
	
}

//在s中寻找匹配模式串m

int KMP(string s ,string m,int* next)
{
	getNext(m,next);
	int i = 0;//s的游标 
	int j = 0;//m的游标 
	int s_len = s.size();//要用一个变量存字符串的长度 
	int m_len = m.size();
	while (i < s_len && j < m_len)
	{//如果写 i <s.zie() && j < size() devc++不认,进不去循环 
		if (s[i] == m[j] || j == -1)
		{
			i++;
			j++;	
		} 
		else
		{
			j = next[j];
		}

	}
	if ( j == m_len)
	{
		return i - j + 1;
	}
	return -1;
}
 
int main()
{
	int next[100] = {0};
	string s,m;
	s = "ABAABAABBABAAABAABBABAAB";
	m = "ABAABBABAAB"; 
	if (KMP(s,m,next)==-1)
	{
		cout<<s<<"  中不存在  "<<m<<endl; 
	}
	else
	{
		cout<<"匹配成功,目标锁定在 "<<KMP(s,m,next)<<" 处"<<endl; 
	}
	return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BF算法和KMP算法都是串的模式匹配算法,但是它们的时间复杂度不同。BF算法的时间复杂度为O(m*n),其中m和n分别为主串和模式串的长度。而KMP算法的时间复杂度为O(m+n)。因此,当模式串较长时,KMP算法的效率更高。 下面是BF算法和KMP算法的介绍和演示: 1. BF算法(暴力匹配算法) BF算法是一种朴素的模式匹配算法,它的思想是从主串的第一个字符开始,依次和模式串的每个字符进行比较,如果匹配成功,则继续比较下一个字符,否则从主串的下一个字符开始重新匹配。BF算法的时间复杂度为O(m*n)。 下面是BF算法的Python代码演示: ```python def BF(main_str, pattern_str): m = len(main_str) n = len(pattern_str) for i in range(m-n+1): j = 0 while j < n and main_str[i+j] == pattern_str[j]: j += 1 if j == n: return i return -1 # 测试 main_str = 'ababcabcacbab' pattern_str = 'abcac' print(BF(main_str, pattern_str)) # 输出:6 ``` 2. KMP算法(Knuth-Morris-Pratt算法) KMP算法是一种改进的模式匹配算法,它的核心思想是利用已经匹配过的信息,尽量减少模式串与主串的匹配次数。具体来说,KMP算法通过预处理模式串,得到一个next数组,用于指导匹配过程中的跳转。KMP算法的时间复杂度为O(m+n)。 下面是KMP算法的Python代码演示: ```python def KMP(main_str, pattern_str): m = len(main_str) n = len(pattern_str) next = getNext(pattern_str) i = 0 j = 0 while i < m and j < n: if j == -1 or main_str[i] == pattern_str[j]: i += 1 j += 1 else: j = next[j] if j == n: return i - j else: return -1 def getNext(pattern_str): n = len(pattern_str) next = [-1] * n i = 0 j = -1 while i < n-1: if j == -1 or pattern_str[i] == pattern_str[j]: i += 1 j += 1 next[i] = j else: j = next[j] return next # 测试 main_str = 'ababcabcacbab' pattern_str = 'abcac' print(KMP(main_str, pattern_str)) # 输出:6 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值