【数据结构】字符串匹配之KMP算法

 

虽然今年寒假时已经结合王道机试指南过了一遍KMP算法,然鹅。。笔者今天又花了大半天才弄清楚KMP算法的工作原理。为了避免重复今天的失态,在本文中整理了KMP的内容。

 


 

1. 基本原理

首先介绍什么是模式匹配?

  • 存储结构——顺序结构
  • 什么是模式匹配?给定两个串S=”s1s2…sn”和T=”t1t2…tm”,在主串S中寻找子串T的过程称为模式匹配。
  • T称为模式串,如果匹配成功,返回T在S中的位置,如果匹配失败,返回0.

因为笔者的文笔一般,理解的也不算多透彻,在这里放一篇大佬整理的关于朴素的模式匹配算法和KMP算法的教程帖:

https://blog.csdn.net/daijin888888/article/details/70212563

文中令我印象最深刻的是这句:(即T前部=T后,T后=S后,则T前=S后

细品之后才发现,这句话道出了KMP算法的核心原理,在搜索时进行跳转的原因和具体跳转到什么地方,都和这句话有关。

详细的内容,请读者自己翻看上文或者找别的经验贴吧。下面开始编程:

 


 

2. 朴素的模式匹配算法

人称“暴力算法”,耗时耗内存。讲道理,笔者也不知道在机试的时候能不能用这个。如果测试用例比较多的话,用这个方法应该可以拿到较小的测试用例的分数吧。虽然是杀敌一千,自损八百的操作。。。

#include<iostream>
#include<cstdio>

using namespace std;

int BF(char S[], char T[])
{
	int i = 0, j = 0;
	while (S[i] != '\0'  &&  T[j] != '\0')
	{
		if (S[i] == T[j]) { i++; j++; }   // 继比较后继字符
		else { i = i - j + 1;   j = 0; }     // 指针后退重新开始匹配
	}
	if (T[j] == '\0')  return i - j + 1;
	else  return 0;
}

int main()
{
	char a[] = "ldncdncdnckmsajnabjnksn";
	char b[] = "nck";
	int position = BF(a, b);
	cout << position << endl;

	return 0;
}

 


 

3. KMP算法

本文核心,但是能说的是不是说完了呢???

建议在阅读代码的时候,自己在VS上逐步调试或者在纸上写写匹配的过程,一定要和理论的算法对应起来。

有的代码是搜索数字,用int 数组存储;有的代码是搜索字符串,用string存储。在本文中,考虑的是字符串,代码如下:

#include<iostream>
#include<cstdio>
#include<string>

using namespace std;

const int MAXN = 1000000;
int nextTable[MAXN];

void GetNextTable(string pattern)
{
	int m = pattern.size();
	int i = 0;
	int j = -1;
	nextTable[i] = j;

	while (i < m)
	{
		if (j == -1 || pattern[i] == pattern[j])
		{
			i++;
			j++;
			nextTable[i] = j;
		}
		else
		{
			j = nextTable[j];
		}
	}

	//for (int i = 0; i < m; i++)
	//{
	//	cout << pattern[i] << " : " << nextTable[i] << endl;
	//}

	return;
}

int KMP(string text, string pattern)
{
	GetNextTable(pattern);
	int n = text.size();
	int m = pattern.size();
	int i = 0;
	int j = 0;
	int number = 0;
	while (i < n)
	{
		if (j == -1 || text[i] == pattern[j])
		{
			i++;
			j++;
		}
		else
		{
			j = nextTable[j];
		}
		if (j == m)
		{
			number++;
			j = nextTable[j];
		}
	}

	return number;
}

int main()
{
	int caseNumber = 0;
	string text, pattern;
	cin >> caseNumber;

	while (caseNumber--)
	{
		while (cin >> pattern >> text )
		{
			printf("%d\n", KMP(text, pattern));
		}
	}

	return 0;
}

 


 

4.总结

虽然花费了一整天来理解这个算法,我觉得还是挺值的。如果脑子不够灵光,就用努力和时间去弥补。不管是眼下的保研,还是未来的工作,皆是如此。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值