C++ 数据结构之KMP算法 详解

KMP算法解析

KMP算法是一种匹配算法,用来进行匹配查找。
通过在子串的每一位都设置一个与之对应的回溯数组下标,降低算法的时间复杂度。
每一个需要查找的子串,该算法都会给它生成一个与之相对于的next数组,用来进行回溯。

假设需要查找的子串为:string S=“aaababa”//该串首位也用来存放数据
生成的next数组为0012010//代表数组下标

假如数组是匹配到了S[3]才出现匹配失败,这代表前面的S[0],S[1],S[2]都匹配成功了。S[0],S[1]和S[1],S[2]是一样的,都是aa,所以现在主串只需要和S[2]进行匹配。

设置子串的next数组的时候,只需要看该数位前面的数据,头和尾一样的数据有多少,回溯的位置就是一样的数据段中串头数据段的下一位!
子串ababc,那么next[4]=2;因为c前面的串头ab和串为ab一样,所以只需从一样的前面部分的下一位开始匹配。
子串ababac,那么next[5]=3,因为c前面串头aba和串为aba一样,同理即可!
子串aab,next[1]=0;next[2]=1;前面是因为没有形成串头串尾,后面的同理即可!
(中间的元素可重复利用,但是子串的串头和串尾不能是同一个元素)

假如子串为S;主串为T。子串与主串进行匹配的时候,如果主串T[j]匹配到了子串S[i]出现匹配失败,那么子串的数组下标i=next[i],然后继续和T[j]进行匹配,一直重复,直到匹配成功或者匹配到了子串的第一个数据依然不相等,则令主串的数组下标指向自增(j++),也就是主串的下一个元素继续和子串进行匹配,一直重复直到找到该子串或者主串结束。

字符串首位存放字符长度

#include
using namespace std;
int getNext(string s, int* next)/获取next数组
{
	int i = 0, j = 1;//i用来代表该回溯的数组下标
	next[1] = 0;
	while (j<(s[0]-48))//因为传进来的是字符,所以想要获得自己想要的数需要在此基础上-48,下面同理
	{
		if (i==0||s[i] == s[j])
		{
			i++;
			j++;
			if (s[i]==s[j])
			{
				next[j] = next[i];
			}
			else
			{
				next[j] = i;
			}
		}
		else
		{
			i = next[i];
		}
	}
	return 0;
}
int search(string t, string s, int pos)//t是主串,s是匹配串,pos是开始匹配的位置
{
	int j = pos;
	int i = 1;
	int next[255];
	getNext(s, next);
	while (j <= (t[0]-48) && i <= (s[0]-48))
	{
		if (i==0||t[j] == s[i])//如果匹配到s[1]还匹配不成功的话,主串就指向下一个元素
		{
			i++;
			j++;
		}
		else
		{
			i = next[i];
		}
	}
	if (i > (s[0]-48))//此时t[j]指的是主串刚匹配完最后一个子串数据的下一个单位
	{
		return  j - (s[0]-48);//减去子串长度得到匹配的首地址
	}
	else
	{
		return 0;
	}
}
int main()
{
	string s = "8aaababaa";//前面的8指的是后面的字符串长度
	string t = "3baa";
	int a;
	a=search(s, t, 0);//如果返回的数是0,则代表主串中没有所要查找的子串
	if (a == 0)
	{
		cout << "查找失败,主串中没有该子串!";
	}
	else
	{
		cout << "所查找的子串的首元素数组下标为:" << a << endl;
	}
	return 0;
}

字符串全部用来存放数据

#include
using namespace std;
int getNext(string s, int *next)//获取next数组
{
	int i = 0, j = 1;//i用来代表该回溯的数组下标,//j代表设置next[j]的值
	next[0] = 0;
	while (j<s.size())//判断串中的元素都设置了回溯值
	{
		if (s[i] == s[j])//如果两者相等,那么主串的元素和s[i],s[j]比较的结果都是一样的,所以直接和最前面那个比较就行了。
		{
			next[j] = next[i];
			i++;
			j++;
		}
		else if (i == 0 && s[i] != s[j])//i一直回溯到第一个还是和s[j]不一样,那么设置next[j]就为i,并且j自增,开始设置下一个值的回溯值
		{
			next[j] = i;
			j++;
		}
		else //如果上面两个条件都没进入,代表既不相等,也还没到第一个匹配值,继续往前回溯
		{
			i = next[i];
		}
	}
	return 0;
}
int search(string t, string s, int pos)//t是主串,s是匹配串,pos是开始匹配的位置
{
	int j = pos;
	int i = 0;
	int next[255];
	getNext(s, next);
	while (j <= t.size() && i <= s.size())//主串中没有该子串,或者子串匹配完毕就退出。
	{
		if (t[j] == s[i])//两个数据相等,就一起对比下一个数据
		{
			i++;
			j++;
		}
		else if (i == 0 && t[j] != s[i])//如果匹配到s[0]还匹配不成功的话,主串就指向下一个元素
		{
			j++;
		}
		else//如果上面的判断都没进入,子串就继续往前回溯
		{
			i = next[i];
		}
	}
	if (i > s.size())//此时t[j]指的是主串刚匹配完最后一个子串数据的下一个单位
	{
		return  j - s.size();//主串上面退出时j所代表的下标刚好是匹配完子串之后的下一个元素,减去子串长度得到主串中与子串相匹配的首地址
	}
	else
	{
		return 0;
	}
}
int main()
{
	string s = "asdfasgcaadaaaabbaaas";//主串
	string t = "aaaabba";//子串
	int a;
	a=search(s, t, 0);
	cout << "所查找的子串的首元素数组下标为:" << a << endl;
	int i = 0;
	while (i<t.size())
	{
		cout << s[a];
		a++;
		i++;
	}
	return 0;
}

你可能感兴趣的:(数据结构,字符串,数据结构,算法)