KMP模式匹配算法&next数组优化代码

KMP是为了解决朴素匹配算法的低效率问题。

例如:

朴素算法匹配:

观察字串第一个字母a于后面的bcdex都不相等,而在①匹配可知,主串和子串的前五位分别相等,意味着子串的首字母a不可能与主串的第2位到第5位的字符相等,所以朴素算法中的②③斯⑤都是多余的。

例如:

T[1]=a,T[2]=b;S[2]=b;

∵T[1]≠T[2]且T[2]=S[2];∴T[1]≠S[2]

同样,在上图子串中 首字符a与后面字符均不相等的前提下,子串的a与主串后面的b,c,d,e也都可以在①之后就可以确定是不相等的,所以在朴素算法中的②③④⑤是多余的,只需保留①⑥即可。而保留⑥是因为在中匹配时,T[6]≠S[6],尽管知道T[1]≠T[6],可也无法确定T[1]≠S[6];所以仍需要判断一下。

当子串中含有重复字符时:

例如:S=abcababca,T=abcabx

根据 上面的描述:T中首字符a与后面的b,c不相等,所以②③是多余的。

∵T[1]=T[4],T[2]=T[5],在①时,T[4],T[5]已经和S中的S[4]S[5]匹配过是相等的

∴T[1]=S[4],T[2]=S[5]

因此④⑤这两个步骤也是多余的。

在朴素算法中,i是不断回溯的,从①-6,到②-2,③-3,④-4,⑤-5,到了⑥,i又变成6.KMP就是为了让没必要的回溯不发生。

也就是i的值不可以变小,那么要考虑的变化就是j的值,在上面的推导就知道,j的值是由子串中是否有重复字符来决定的。

例如:

T=abcdex,T没有重复字符,j就从6变回了1.而

T=abcabx,前缀的ab和最后x之前串的后缀ab是相等的,因此j就由6变成了3.因此,j值的多少取决于当前字符之前的串的前后缀的相似度。

现在把T串各个位置的j值的变化定义为一个next数组,那么next数组长度就是子串的长度,所以:

               0,j=1

next={  Max{k|1<k<j,且‘p1p2....pk-1'(前缀)='pj-k+1.....pj-1'(后缀)}

               1,其他情况

要匹配的子串的next数组代码实现:

void get_next(String T, int *next)
{
	int i, j;
	i = 1; 
	j = 0;
	next[1] = 0;
	while (i < T[0])//T[0]表示子串长度
	{
		if (j == 0 || T[i] == T[j])
		{
			i++;
			j++;
			next[i] = j;
		}
		else
			j = next[j];//若字符不相同,则j值回溯
	}
}

 KMP算法改进:

当S=aaaabcde     T=aaaaax,其next数组的值为{012345}

其实,当中的②③④⑤ 步骤是多余的,因为T中第二,三,四,五位置的字母都与首位a相等,那么既可以用首位next[1]的值去代替与它相等的字符后续next[j]的值。

代码:

void get_nextval(string T, int *nextval)
{
	int i = 1;
	int j = 0;
	nextval[1] = 0;
	while (i < T.length())
	{
		if (j == 0 || T[i] == T[j])
		{
			i++;
			j++;
			if (T[i] != T[j]])//若当前字符与前缀不同
				nextval[i] = j;//则当前的j为nextval在i位置的值
			else
				nextval[i] = nextval[j];//如果相同,则将前缀的nextval[i]值赋值给nextval在i位置的值
		}
		else
			j = nextval[j];
	}
}

下面是从0开始匹配的完整代码。

完整代码:

#include<iostream>
#include<string>
using namespace std;
/*void get_next(string t, int next[])
{
	int i = 0;
	int j = -1;
	next[0] = -1;
	while (i < t.length())
	{
		if (j == -1 || t[i] == t[j])//t[i]表示后缀的单个字符,t[j]表示前缀的单个字符
		{
			i++;
			j++;
			next[i] = j;
		}
		else
		{
			j = next[j];//若字符不想的,则j值回溯,即缩短比较的前后缀长度。
		}
	}
}
*/
void get_nextval(string t, int nextval[])
{
	int i = 0;
	int j = -1;
	nextval[0] = -1;
	while (i < t.length())
	{
		if (j == -1 || t[i] == t[j])
		{
			i++;
			j++;
			if (t[i != t[j]])//若当前字符与前缀不同
				nextval[i] = j;//则当前的j为nextval在i位置的值
			else
				nextval[i] = nextval[j];//如果相同,则将前缀的nextval[i]值赋值给nextval在i位置的值
		}
		else
			j = nextval[j];
	}
}
int Index(string s, string t, int pos)
{
	int i = pos;//从pos位置开始匹配
	int j = 1;//子串当前位置下标值
	int size1 = s.length();
	int size2 = t.length();
	int next[222];
	get_nextval(t, next);
	while (i < size1&&j <size2)
	{
		if (j == -1 || s[i] == t[j])//若字符相等就继续判断
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];//j退回适合的位置,i值不变
		}
	}
	if (j >size2-1)
		return i - size2+1;
	else
		return 0;
}
int main()
{
	string S, T;
	cin >> S >> T;
	int num = Index(S, T, 0);
	if (!num)
		cout << "NO found!" << endl;
	else
		cout << "从" << num-1 << "开始查找" << endl;
	return 0;
}

next数组的性质,有
i%( i-next[i] )==0&&next[i] !=0 , 则说明字符串前i位循环,而且循环节长度为:i-next[i],循环次数为: i/( i-next[i]);

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 首先,KMP算法是一种字符串匹配算法,它的核心是通过预处理模式串,得到一个next数组,用于在匹配过程中快速跳过已经匹配过的部分。 在创建顺序串时,我们需要定义一个结构体来表示顺序串,包含一个字符数组和一个整型变量表示串的长度。在实现KMP算法时,我们需要预处理模式串,计算出next数组优化next数组的方法是通过递归计算前缀和后缀的最长公共前缀长度,避免重复计算。 具体实现步骤如下: 1. 定义顺序串结构体,包含字符数组和长度变量。 2. 实现KMP算法,预处理模式串,计算出next数组。 3. 优化next数组,通过递归计算前缀和后缀的最长公共前缀长度,避免重复计算。 4. 在匹配过程中,利用next数组快速跳过已经匹配过的部分,提高匹配效率。 总之,KMP算法是一种高效的字符串匹配算法,通过预处理模式串,得到一个next数组,用于在匹配过程中快速跳过已经匹配过的部分。在实现KMP算法时,需要注意优化next数组的方法,避免重复计算,提高匹配效率。 ### 回答2: KMP算法是一种字符串匹配算法,它的核心思想是利用已匹配成功的部分信息,来避免在不必要的地方做重复的比较。在KMP算法中,next数组是重要的组成部分,它存储了模式串的前缀中最长的相等的前缀和后缀的长度。 创建顺序串实现KMP模式匹配,可以通过以下步骤: 1.创建一个顺序串,可以使用数组或者类似于字符串的方式实现。 2.读入模式串和文本串,将它们分别存入两个顺序串中。 3.通过next数组优化模式串的匹配。next数组的计算过程可以采用递归或迭代的方式实现。 4.在匹配时,从文本串的第一个字符开始,依次比较模式串的字符和文本串的字符,如果匹配则继续比较下一个字符。如果不匹配,就利用next数组跳过已经匹配成功的部分,继续向后匹配。 5.如果匹配成功,则返回匹配的位置;否则继续比较,直到文本串的所有字符都比较完毕。 优化next数组的计算,可以通过以下步骤: 1.初始化next数组,将第一个元素设为0,第二个元素设为1。 2.从第3个元素开始,依次比较前一个元素和模式串的前缀后缀。 3.如果相同,则将next数组中的元素赋值为前一个元素的值加1;否则继续比较下一个前缀和后缀。 4.如果没有相同的前缀和后缀,则将next数组中的元素赋值为0。 5.重复以上步骤,直到计算出next数组中所有的元素。 通过以上步骤,我们就可以创建顺序串并实现KMP模式匹配优化next数组。这个算法可以有效地提高匹配的效率,节约时间和空间。 ### 回答3: KMP模式匹配算法是一种高效的字符串匹配算法,它的核心在于构建next数组。而优化next数组的过程,可以通过创建顺序串的数据结构来实现。 首先,我们需要了解什么是顺序串。顺序串是一种基于数组的线性数据结构,可以用来存储字符序列。我们可以通过封装数组的方式,添加一些操作方法来实现顺序串的创建。具体来说,我们可以定义如下的结构体: ``` typedef struct SeqString { char* data; // 数据存储区 size_t length; // 串的长度 } SeqString; ``` 其中,`data`成员用于存储串的数据,`length`表示串的长度。接下来,我们可以定义创建顺序串的函数: ``` SeqString* createSeqString(char* str, size_t len) { SeqString* s = (SeqString*)malloc(sizeof(SeqString)); s->data = (char*)malloc((len + 1) * sizeof(char)); memcpy(s->data, str, len); s->data[len] = '\0'; s->length = len; return s; } ``` 在创建顺序串之后,我们可以对其进行遍历,构建next数组KMP模式匹配算法中的next数组表示模式串中,以每个位置为结尾的子串中,前缀和后缀相等的最大长度。为了构建next数组,我们可以采用两个指针i和j,分别指向模式串的第一个字符和第二个字符,并通过遍历模式串的方式,依次计算得到next数组。 具体的算法流程如下: 1. 初始化i=0,j=-1,next[0]=-1 2. 判断j是否小于0或者s[i] == s[j],如果满足,则令i=i+1,j=j+1,next[i] = j 3. 如果s[i] ≠ s[j],则更新j=next[j] 4. 重复进行步骤2和3,直到i等于模式串的长度为止 通过这种方式构建得到的next数组,可以在模式匹配过程中,实现更高效的匹配。具体的匹配过程,可以采用next数组计算当前的模式串与主串的匹配偏移量,通过不重复的移动主串和模式串的方式,来实现快速的匹配。 总的来说,优化next数组的过程是非常重要的,可以大大提升KMP模式匹配算法的效率。通过创建顺序串的数据结构,我们可以方便地实现这一优化过程,得到更高效的字符串匹配算法

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值