一次搞懂KMP算法

最近又复习了下KMP算法,有些东西长久不用还是会忘。百度了很多,终于找到了一篇思路比较清晰,讲解中能突出重点的帖子:
https://blog.csdn.net/f1033774377/article/details/82556438
总结一下,算法中的几个重点,以作备忘。其中的F数组即next数组:

  1. F数组:数组索引对应模式字符串的索引,元素值“0到索引值位置”子串的最长前缀结束字符的索引位置。如模式字符串“abacd”,F(2)=0,即0~2这部分子串,其最长前缀的结束字符位置为0,也就是子串"a",所以F数组中的元素保存到都是模式字符串的字符位置。
  2. 字符串与模式串的匹配过程,若出现字符不同的位置j(模式字符索引),应当找匹配部分最后一个字符位置,即j-1,的最长前缀位置F(j-1),并用F(j-1)+1作为下一次开始匹配时的开始位置。其实就是做了一次模式字符串相对于比对字符串后移,后移达到的效果将最后一个匹配字符的位置对齐到最长前缀的尾字符。相当于跳过了F(j-1)+1至j-1部分,其合理性在于:从这个区间内的任意位置j’开始匹配,匹配长度不会超过j-j’(此时,F(j-1)
  3. F数组的构造其实就一个思路,计算F(k)时要参考F(k-1)的值,因为二者存在关联关系。即模式字符串p中,若p[k] == p[F(k-1)+1],则F(k) == F(k-1)+1。

按照理解,补充代码实现:

#include "stdafx.h"
#include 
#include 

using namespace std;

void F(const string& pattern, std::vector<int>& f)
{
	f.resize(pattern.size());
	f[0] = -1;

	for (int k = 1; k < pattern.size(); ++k)
	{
		int j = f[k - 1];
		while (pattern[j + 1] != pattern[k] && j >= 0)
			j = f[j];

		if (pattern[j + 1] == pattern[k])
			f[k] = j + 1;
		else
			f[k] = -1;
	}
}

void KMP(std::string str, std::string pattern)
{
	std::vector<int> f;
	F(pattern, f);

	if (str.size() < pattern.size())
		return;

	int j = 0, m = pattern.size();
	for (int k = 0; k < str.size(); )
	{
		if (str[k] == pattern[j])
		{
			++k, ++j;
			if (j == m)
			{
				std::cout << "found match: " << k - m << std::endl;
				j = f[m - 1] + 1;
			}
		}
		else
		{
			if (j == 0)
				++k;
			else
				j = f[j - 1] + 1;
		}
	}
}

你可能感兴趣的:(数据结构)