KMP算法

缘起:

题目链接:串的模式匹配

资料

图解看这个
代码借鉴

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define NotFound -1
#define maxn 1000000
typedef int Position;
char str[maxn], pattern[maxn];
int match[maxn] = { 0 };
void BuildMatch();
Position KMP();
int main()
{
	cin >> str;
	cin.get();
	//作用和getchar()一样
	//如果cin>>str+1,这样str[0]='\0',因为地址往后移了一格
	//使用 cin>> 就不可能仅输入一个空格或回车符
	int n;
	cin >> n;
	cin.get();
	for (int i = 0; i < n; i++)
	{
		cin >> pattern;
		cin.get();
		BuildMatch();
		Position p=KMP();
		if (p == NotFound) cout << "Not Found" << endl;
		else cout << str + p<<endl;
	}
	return 0;
}
Position KMP()
{
	int len_pat = strlen(pattern);
	int len_str = strlen(str);
	int i = 0,j = 0;
	while (i < len_pat && j < len_str)
	{
		if (i == -1 || pattern[i] == str[j])
		{
			i++;
			j++;
		}
		else
			i = match[i];		
	}
	if (i == len_pat) return j-i;
	else return NotFound;
}
void BuildMatch()
{
	match[0] = -1;
	int i = 0, j = -1;
	while (i < strlen(pattern))
	{
		if (j == -1 || pattern[i] == pattern[j])
		{
			i++;
			j++;
			match[i] = j;
		}
		else j = match[j];
	}
}

个人理解

谈一下个人对这个代码的理解
首先是void BuildMatch()函数
这个函数求match(现在广泛使用的是next)数组的过程完全可以看成字符串匹配的过程,即以模式字符串的前缀为目标字符串,一旦字符串匹配成功,那么当前的match值就是匹配成功的字符串的长度

void BuildMatch()
{
	match[0] = -1;
	int i = 0, j = -1;
	while (i < strlen(pattern))
	{
		if (j == -1 || pattern[i] == pattern[j])
		{
			i++;//pattern往后移动
			j++;//如果匹配匹配的字符串长度++/j==-1 j++ 匹配长度为0
			match[i] = j;//match[i]表示i位置前的最长匹配字符串长度
		}
		else j = match[j];
		//一旦没法继续匹配,匹配的起始地点就归位到最开始的字符
		//这里补充解释一下,本来应该是找到相匹配的前后缀子串的下一个字符与当前字符相比较,
		//因为数组开始是从0开始,match[j]是该字符最长匹配子串的长度,所以pattern[match[j]]就是前后缀子串的下一个字符的位置(看图解就很清楚了)
	}
}

Position KMP()函数
这块看图解就能明白原理
在c前面的最长匹配子串为4,那么a前面的六位子串中abab(前四)与abab(后四)相同
也就是说c前面的abab(前四)与a前面的六位子串的abab(后四)相同这一段就不用比较了,直接比较i指向的a和子串的第五位
在这里插入图片描述

Position KMP()
{
	int len_pat = strlen(pattern);
	int len_str = strlen(str);
	int i = 0,j = 0;
	while (i < len_pat && j < len_str)
	{
		if (i == -1 || pattern[i] == str[j])
		{
		//如果字符相匹配的话那么子串和主串所指字符的位置都后移一位
			i++;
			j++;
		}
		else
		//如果不匹配,找该位置前面的最长匹配子串
		//因为前后一直,子串的前部分与主串的后部分相同,没有必要继续比较
			i = match[i];		
	}
	if (i == len_pat) return j-i;
	else return NotFound;
}

碎碎念:
一开始是用find的,第五个测试点超时,然后问大佬,给的解释的find的内部原理起始就是暴力搜索,那就学一下kmp吧
c语言中有个strstr函数好像可以实现,下次试试看,内部是啥算法的实现等我查查再补充


可算是把KMP学完了,一直理解不了,我真的太菜了

posted on 2021-05-07 22:05  不依法度  阅读(34)  评论(0)    收藏  举报

导航