数据结构 串模式匹配 KMP算法

【数据结构】 串 KMP算法实现


KMP算法应用于串的模式匹配中


普通模式匹配算法在进行匹配时需要频繁对主串指针进行回溯,KMP算法通过将模式向右滑动一段距离的方式避免了主串的回溯,同时降低了算法复杂度 ,由原来的O(n*m)变为O(n)。

KMP算法本身比较容易理解,就是对模式串本身的结构进行分析,在匹配过程中跳过一些不必要的步骤。
比如当模式串为ababc; 主串为ababdddd时; 匹配进行到第五个字符处时失败了,但是这时主串指针不必进行回退(又重新从第二个字符b处开始比较),只需要根据模式串的 nest 函数,将模式指针进行移动,再进行下一次判断。
在这里插入图片描述


next函数求解

KMP算法难以理解的点在于 next 函数的求解
要弄明白一些基本的东西:
在比较的过程中,存在着两个指针,

  1. 主串指针,指向当前主串正在进行比较的字符
  2. 模式串指针,指向模当前正在匹配的模式串的字符位置
    在使用KMP算法时,主串是不会进行回溯的,只有右移操作,主要是对模式串指针的滑动操作。

算了,写不下去了

弄懂之后才知道为什么书上写得那么晦涩难懂了。因为实在是很难说明白

1.递推

2.这个问题可以看成一个模式匹配问题,整个模式串既是主串,又是模式串。

3.在求解 next【n+1】的过程中,其实还是在进行模式匹配,可以通过已知的next来获得next【n+1】

4.后面的优化:对于 第 j 个字符 ,如果与next [ j ]所指示的字符相等,那么 next [ j ] = next [ next[j] ].


想要理解整个算法,建议自己照着书上的思路走一遍,写一遍代码,模拟整个函数的执行过程,差不多懂了之后再回过头看看定义 (网上很多详解都没有把整个思路讲清,还得自己悟)

代码部分:

下面是KMP算法的实现,代码中串采用堆分配存储表示,书中串的存储方式为定长顺序串,代码略有区别

1.采用KMP算法的模式匹配函数
int Index_KMP(HString S, HString T, int pos,int next[]) {
	//利用模式串T的next函数求T在主串中第pos个字符之后的位置的KMP算法。
	int i = pos;
	int j = 0;
	while (i<S.length&&j<T.length)
	{
		if (j == -1 || S.ch[i] == T.ch[j]) { ++i; ++j; }
		else
		{
			j = next[j];
		}
	}
	if (j >= T.length)return i - T.length+1;	//匹配成功
	else return -1;								//输出-1失败
}

2.KMP算法中next函数求取过程

void get_next(HString T, int next[]) {
	//求模式串T的next函数值并存入数组next
	int i = 0; next[0] = -1; int j = -1;
	while (i < T.length-1) {
		if (j == -1 || T.ch[i] == T.ch[j]) { ++i; ++j; next[i] = j; }
		else
		{
			j = next[j];
		}
	}
	//get_next
}

3.next函数求取优化

void get_nextval(HString T, int nextval[]) {
	int i = 0; nextval[0] = -1; int j = -1;
	while (i < T.length - 1) {
		if (j == -1 || T.ch[i] == T.ch[j]) { 
			++i; ++j;
			if (T.ch[i] != T.ch[j])nextval[i] = j;
			else nextval[i] = nextval[j];
		}
		else
		{
			j = nextval[j];
		}
	}
	//get_nextval
}

4.测试部分

#include"String.h"
#include"KMP.h"
using namespace std;

void StrDisplay(HString S) {
	//显示串
	for (int i = 0; i < S.length; i++)
	{
		cout << S.ch[i];
	}
}
int main() {
	HString A;
	StrInit(A);
	HString B;
	StrInit(B);
	HString C;
	StrInit(C);
	char b[20] = "ababbbabaabcaccc";
	char c[10] = "abaabcac";
	int next[10] = { -1,0,0,1,1,2,0,1 };
	cout << "初始化B,显示B:" << endl;
	StrAssigh(B, b);
	StrDisplay(B);

	cout << endl<<"初始化C,显示C:" << endl;
	StrAssigh(C, c);
	StrDisplay(C);
	
	cout << endl << "对B,C串进行模式匹配:"<<endl;
	cout << Index_KMP(B, C, 1, next) << endl;
	cout << "输出C的next函数:" << endl;
	get_next(C, next);
	for (int  i = 0; i < 8; i++)
	{
		cout << next[i]<<" ";
	}
	cout << endl;
	cout << "输出改进后C的next函数:" << endl;
	get_nextval(C, next);
	for (int i = 0; i < 8; i++)
	{
		cout << next[i]<<" ";
	}
	system("pause");
}

结果

在这里插入图片描述

注:使用堆串实现,与书中结果相差1 (因为堆串的首地址是有值的,定长顺序串首地址 放的是串长)

发布了17 篇原创文章 · 获赞 14 · 访问量 6826
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览