字符串的两种模式匹配算法--暴力法与KMP算法

对于字符串而言,最常见的基本操作莫过于查找某一字符串(模式串)在另一字符串(主串)中的位置,这一操作过程叫做字符串的模式匹配,常见的模式匹配算法有朴素模式匹配算法和KMP模式匹配算法,下面结合代码对这两种模式匹配算法的思想做个总结。
参考博客:很详尽的KMP算法(厉害)

1.朴素模式匹配算法(暴力法)

朴素模式匹配算法的思想就是,把主串中的每一个字符作为子串开头,与要匹配的字符串进行逐字符匹配,直到所有字符匹配成功或全部遍历完成为止。

#include <iostream>
using namespace std;

/* 返回模式串P在主串S中的位置,若不存在,则函数返回值为-1 */
int index(const char* s, const char* p)
{
	int sLen = strlen(s);
	int pLen = strlen(p);
	int i = 0;	// i用于主串s中当前位置的下标
	int j = 0;	// j用于模式串p中当前位置的下标
	while (i < sLen && j < pLen)
	{
		if (s[i] == p[j])	// 两字符相等,则递增i和j,比较下一位置字符
		{
			i++;
			j++;
		}
		else	// 两字符不等
		{
			i = i - j + 1;	// 回退i,回到上次匹配开始的首位的下一位
							/* 此时T[j]与S[i]匹配失败,而j是从0开始递增的,表明前j个字符匹配都是成功的,
							   由此可得到i是从(i-j)开始递增的,则下次匹配从(i-j+1)开始。
							*/
			j = 0;			// 将j重置为0
		}
	}
	
	if (j == pLen)
	{
		return i - j;
	}
	else
	{
		return -1;
	}
}


int main() {
	const char* S = "ABCDABD";
	const char* P = "ABD";
	int ret = index(S, P);
	cout << "ret = " << ret << endl;
}

2.KMP模式匹配算法

KMP算法相比朴素模式匹配算法效率提升了很多,因为它避免很多不必要的比较操作,KMP算法过程只更新j,而不回退i,前提是先计算出表示模式串下标j的变化数组next,next 数组各值的含义:代表当前字符之前的字符串(下标为0,1,…,j-1)中,有多大长度的相同前缀后缀,例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。

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

/* 计算KMP算法中模式串P下标j的变化数组next */
void get_next(string P, int* next) {
	int pLen = P.size();
	next[0] = -1;
	int k = -1;	// 前缀末尾字符下标
	int j = 0;	// 后缀末尾字符下标
	while (j < pLen - 1)
	{
		// p[k]表示前缀,p[j]表示后缀 
		if (k == -1 || P[k] == P[j])
		{
			++k;
			++j;
			next[j] = k;
		}
		else
		{
			k = next[k];
		}
	}
}

/* KMP算法:返回模式串P在主串S中的位置,若不存在,则函数返回值为-1 */
int index_KMP(string S, string P, int* next) {
	int sLen = S.size();
	int pLen = P.size();
	int i = 0;
	int j = 0;
	while (i < sLen && j < pLen)
	{
		/*
		备注:
		这里的 j == -1,是考虑到next[0]值为-1的情况,若j为-1,就不能作为P[j]的索引了
		*/
		if (j == -1 || S[i] == P[j])	// 当前字符匹配成功,继续匹配下一字符
		{ 
			i++; 
			j++;
		}
		else	// 当前字符匹配失败,更新j为next[j],i不变
		{
			j = next[j];
		}
	}
	if (j == P.size())
	{
		return i - j;
	}
	else
	{
		return -1;
	}
}

int main() {
	string S, P;
	cout << "Please input string S: ";
	cin >> S;
	cout << "Please input string P: ";
	cin >> P;

	int* next = new int[P.size()];
	get_next(P, next);

	// print next array
	cout << "next: " << endl;
	for (int i = 0; i < P.size(); i++)
	{
		cout << next[i] << "\t";
	}
	cout << endl << endl;

	int ret = index_KMP(S, P, next);
	cout << "ret = " << ret << endl;
	
	delete[] next;
}

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MinBadGuy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值