KMP算法:C/C++代码实现

前言

KMP算法是很经典且高效率的字符串匹配算法,时间复杂度O(m+n)。而纯暴力的BF算法,时间复杂度为O(mn)。网上解析KMP算法的博客也很多,在后面推荐几个不错的博客理解其原理,在此只贴出代码。

关键

1.首先需要理解前缀、后缀的概念。
比如:字符串"abaab"

前缀“a”“ab”“aba”“abaa”
后缀“b”“ab”“aab”“baab”

2.然后理解next数组的概念
next数组逐个存储前缀等于后缀的个数,即前缀等于后缀的公共字段的个数。
如:
“abaab”:

字符abaabNULL
位置j012345
next[j]-100112
  • 位置j为4时,字符串为:“abaa”,前缀和后缀中有相同的子串"a",所以next[j]=1。
  • 位置j为5时,字符串为:“abaab”,前缀和后缀中有相同的"ab",所以next[j]为2。

注意:因为设置第一个值为-1,所以实际的next[j]就根据当前位置计算next值。和有些文章计算i之前的字符串next[j]的值不一样。同时,如果字符串的索引从1开始的话,那么next[j]的值需要+1。


3.那么next[j]的值到底是什么意思呢?意思就是模式串(短的)从next[j]的值开始的位置和主串进行匹配。因为主串的指针i是一直往后移动的,所以主串和模式串的位置next[j]匹配,就相当于模式串往后移动了。

4.最后编写代码即可。编码时注意理解概念,从理论到实际还是有所不同的。kmp算法,主串的位置指针i是一直往下走的,避免了回溯而浪费时间。

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <iostream>
using namespace std;

void FindNext(string str, int next[])//找到next数组
{
	int i = 0;
	int j = -1;
	int n = str.size();//str的长度

	next[0] = j;//next[0]存储-1,在j=next[j]时,如果j=0,则next[j]=-1,下次判断就可以进入第一个if
	while (i < n)
	{
		if (j == -1 || str[i] == str[j])//j=-1表示上一次不匹配;两者相等则j++赋值给next
		{
			i++;
			j++;
			next[i] = j;
		}
		else
		{
			j = next[j];//不满足,j跳转
		}
	}
}

//优化后的next数组
void FindNextVal(string str, int next[])
{
	int i = 0;
	int j = -1;
	int n = str.size();//str的长度

	next[0] = j;//next[0]存储-1,在j=next[j]时,如果j=0,则next[j]=-1,下次判断就可以进入第一个if
	while (i < n)
	{
		if (j == -1 || str[i] == str[j])//j=-1表示上一次不匹配;两者相等则j++赋值给next
		{
			i++;
			j++;
			if (str[i] == str[j])//如果当前位置的str值等于当前位置存储的next数组的str值,可不必该次计算
				next[i] = next[j];	//因为已经知道当前不匹配值为str[i],那么str[j]与str[i]相同的话也不匹配
			else
				next[i] = j;
		}
		else
		{
			j = next[j];//不满足,j跳转
		}
	}
}

int KMP(string s, string p, int next[])//s模板串(长的), p匹配串(短的),next数组,返回匹配字符的头所在位置
{
	FindNextVal(p, next);//给匹配串计算next数组

	int i = 0;//s串的位置,一直增加
	int j = 0;//p串位置,根据next数组改变
	int n = s.size();
	int m = p.size();

	while (i < n && j < m)
	{
		if (j == -1 || s[i] == p[j])
		{
			i++;
			j++;
		}
		else
		{
			j = next[j];//j的位置决定了p字符位置
		}
	}

	if (j == m)//说明在s中找到了子串p
	{
		return i - j;
	}
	else
	{
		return -1;
	}
}


int main()
{
	int next[100] = { 0 };
	cout << KMP("abcababbabaabc", "abaab", next) << endl;
}

参考文章

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暗夜无风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值