串的模式匹配

串的模式匹配有两种方法:

1.BF算法:

即暴力解决:主串指针i依次往后移动,当要匹配的字符串指针j所指向的元素与i所指向的元素不相同时,i恢复到原来开始位置的下一个,j恢复到原来状态(0或1),若最初下标是从0开始,i变为i-j+1,若从1开始i变为i-j+2;一次往后移动直到匹配成功或主串结束。

#include<iostream>
#include<cstring>

using namespace std;
//以下标从零开始为准
int main()
{
	int i = 0, j = 0;
	string s1 = "abcdabcefgh";//主串
	string s2 = "abce";
	//***算法核心***
		for (; j < s2.length(); j++)
		{
			if (i >=s1.length())
			{
				cout << "false" << endl;
				return 0;
			}
			else if (s1[i++] == s2[j])
				;
			else
			{
				i = i - j + 1;
				//注意同时也要更新j
				j = 0;
			}
		}
		cout << "true";
		//***算法核心***
		return 0;
}

程序演示结果为true。接下来分析时间复杂度:O(mn)
s1长m,s2长n。考虑最坏的情况,找到最后也没找到。那么在s1的前n个字符分别匹配1,2,3……n次,后面的字符都匹配m次(分别与s2的第一位……第n位匹配),所以时间复杂度几乎是n
m;

2.KMP算法

由BF算法可知,这种方法时间主要浪费在匹配完第一个字符后,直到匹配不上的区间里已经进行过匹配,而再一次进行匹配;KMP算法就避免这种时间消耗,i从当前位置继续,j也不再从零开始,用next[j]储存下一个j直接到达的位置。

那么j下一个要到达哪里呢?

j到达的位置是要与i指向的字符进行比较的,那么i之前的字符(可以是0个)肯定要与j之前的字符(一直到0位)进行匹配的(也可以是0个)。也就是说,在开始进行这一轮比较时,是匹配的;直到不匹配时i之前的字符又与所要匹配串串首匹配了。所以,可以说子串中除串首外还有一部分与串首相同,只要找到这部分与串首相同的(与串首相同,也与所要匹配串串首相同)就可以确定接下来j的位置了。

找与串首相同部分

(1)KMP中借助真前缀子串的真前缀,确保串首。
(2)借助真前缀子串的真后缀,确保非串首部分。
(3)若(1)和(2)中有相同的,(即真前缀子串的真前缀与真前缀子串的真后缀相同),说明找到了与串首相同的部分。

接下来

定位j的位置

若下标从0开始,j就是主串中与串首相同部分的子串的长度;
若下标从1开始,j就是主串中与串首相同部分的子串的长度+1;

到现在为止,我们已经解决了next[j]的问题,接下来就是代码问题了。

#include<iostream>
#include<cstring>
using namespace std;

#define MAX 100

int my_next[MAX];

//求next[j]
void nextj_solve(string s2)
{
	my_next[0] = -1;
	for(int i=1;i<s2.length();i++)
	{
		int j = 1, cnt = 0;
		//求真前缀子串(最后一个元素是i-1)
		string tmp = s2.substr(0, i);
		//int tmp_i = tmp.length();
		//i等于tmp_i
		//下标从零开始的话肯定最大时长度减一,即j<i
		for (; j < i; j++)
		{
			//求真前缀子串的真前缀
			string tmp0 = tmp.substr(0, j);
			//求真前缀子串的真后缀
			string tmp1 = tmp.substr(i - j, i);
			if (tmp0==tmp1 && tmp0.length() != 0)
				//找重合部分最长的
				cnt = max(cnt, j);
		}
		//下标从零开始不用加一
		my_next[i] = cnt;
	}
}

bool kmp_solve(string s1, string s2)
{
	int j = 0, i = 0;
	while (1)
	{
		if (j == s2.length())
			return true;
		if(i==s1.length())
			return false;
		if (j != -1 && s1[i] != s2[j])
			j = my_next[j];
		else
		{
			j++;
			i++;
		}
	}
}
int main()
{
	string s1 = "bbcxabcdabxabcdabcdabde";
	string s2 = "abcdabd";
	nextj_solve(s2);
	if (kmp_solve(s1, s2))
		cout << "true" << endl;
	else
		cout << "false" << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值