【KMP算法】如何求next数组 C语言实现+全代码详解

KMP算法和手算next数组就不多说了,主要是如何代码实现求next数组~

next数组

next[j]: j指向模式串的第j个位置(1开始),next[j]表示模式串第j个字符与主串不匹配时, j要回溯到第几个位置  next[0]弃用
next[j]的值:第j个字符前面的j-1个字符组成的子串 的最大公共前后缀的长度+1(很重要!!)
next[1] = 0;   next[2] = 1;
模式串第j个字符不匹配时,指向主串的i位置不变,向后移动模式串,将最大公共前缀对齐最大公共后缀,j就指向后缀的下一个字符

          ↓
a b a b a c ......
a b a b a a          "ababa"最大公共前后缀为"aba"
    a b a b a a(移动后)

如何求next数组呢?

比如求next[16]: 已知next[15](比如next[15]=7),即前14个字符组成的子串的最大公共前后缀长度为6, 比较第7(next[15])个字符和第15个字符
若相等,则前15个字符的最大公共前后缀为7, 即next[16] = next[15] + 1 = 8;
若不相等,比如next[7]=3, 即前6个字符组成的子串的最大公共前后缀长度为2, 比较第3(next[7])个字符和第15个字符
若相等,则前15个字符的最大公共前后缀为3, 即next[16] = next[7] + 1 = 4;
若不相等,比如next[3]=1, 即前2个字符组成的子串无公共前后缀, 也就是前15个字符没有公共前后缀, 即next[16] = 1;1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
a b c c a b * * a b  c  c  a  b  *  *
    ↑      ↑
            next[15]=7
            
KMP,"看门牌",j的"门牌"就是next[j], 求next[k+1],看第k个字符和它的"门牌号码"(第next[k]个字符)是否相等

画图就很好理解了:这个up主讲得挺好

next数组进一步优化

当第 j 个字符与第 k = next [ j ] 个字符相等时,next [ j ] 可直接等于next [ k ]

代码实现

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define MAX 255

//串
typedef struct {
	char ch[MAX];
	int length;
}SString;

void GetNext(SString t, int next[]) {
	int i = 1;	//i=1指向模式串t的第一个字符位置,也是next数组的索引
	int j = 0;
	next[1] = 0;
	while (i < t.length) {
		if (j == 0 || t.ch[i] == t.ch[j]) {
			next[++i] = ++j;
			if (t.ch[i] == t.ch[j])		//进一步优化
				next[i] = next[j];
		}
		else
			j = next[j];	//若一直不相等,最终j = next[1] = 0
	}
}

//若主串中存在与模式串完全相同的子串,返回第一次出现的位置,不存在则返回0
int Index_KMP(SString s, SString t, int next[]) {
	int i = 1, j = 1;	//i = 1指向s.ch[0], j = 1指向t.ch[0]
	while (i <= s.length && j <= t.length) {
		//当第一个元素匹配失败时,匹配下一个字符 令j = 0后i++,j++
		if (j == 0 || s.ch[i - 1] == t.ch[j - 1]) {
			i++;
			j++;
		}
		else {
			j = next[j];
		}
	}
	if (j > t.length)
		return i - t.length;
	else
		return 0;
}
int main()
{
	SString s = { "fjauirnvoagnirhnabaabchjk",25 };
	SString t = { "abaabc",6 }; //17
	int next[10] = { 0 };
	GetNext(t, next);
	printf("%d", Index_KMP(s, t, next));
	return 0;
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值