KMP算法

描述

        连续多组输入两个字符串a与b,如果b是a的子串,就输出YES,否则输出NO,a与b都只输入字符串“0”,程序终止

输入输出可以自己想哦

KMP基本思想
        按照传统的BF算法,我们如果要对字符串s1=dababcde与s2=abc进行模式匹配(求是否是子串),要从s1与s2的第一位开始比价,s1[0]=d不等于s2[0]=a,然后往后s1[1]=a等于s2[0]=a,s1[2]=b等于s2[1]=b,s1[3]=a不等于s2[2]=c,那么s2又要从0开始,s1更是要从s1[2]重新开始,直到某一次s2可以全部匹配完成。时间复杂度为O(mn)

        KMP算法对流程进行了优化,以s1=ababaaababaa,s2=ababc为例,如果s2的abab都能匹配上,c匹配不上,s2还需要从s2[0]开始吗?我们发现s2[0]-s2[1]与s2[2]-s2[3]相等,那我们就可以不用比较前两位,比如在这个例子中,s1读取了ababa s2读取了ababc 在s1[4]与s2[4]出现分歧,我们只需要让s1仍然留在s1[4]与s2[2]比较即可,时间复杂度为O(m+n)

        那么我们就需要一个数组,数组长度与s2相等,去存取s2的每一个字符出现匹配失败后应该从哪里开始(s1始终保持不变)

        例如:(第一位必须是0,后面解释;示例中next是逻辑地址,

        示例1:     aabbcdabbcd

        next数组:01211112111

        示例2:     ababaaababaa

        next数组:011234223456

        求next[i]数组的过程其实是求next前面的子串和i前面子串相同的数量,abababaaababaa中,s[3]=b 他前面的a能和s[0]=a匹配上,但是ba\aba都不能,所以next[3]=2,即第一位不用比,从第二位开始比较。

        但是我们发现,这样的过程仍不够快,比如ababaaababaa中,s[3]=a与s[0]=a相等,但我们却只能根据s[3]前面的子串判断s[3]应该去哪重新比较,得出的结果是next[3]=1,但是我们明明已经知道指针指向s[0](1是逻辑地址,0是存储地址,这里相当于s[next[i]-1])之后的运算结果也是无法匹配成功,所以我们可以优化一下next数组,如果某个位置的next指向的字符与他自身相等,那就直接改成指向他next指向的next,比如next[3]=1但是s[0]=s[3],所以next[3]=next[0]=0

      示例1:          aabbcdabbcd
      next数组:     01211112111
      nextval数组:00211102111
      示例2:         ababaaababaa
      next数组:    011234223456
      nextval数组:010104210104

理论大家可以多思考一下,列几个字符串求一下他的next和nextval数组,熟悉之后直接写代码就行了,多注意逻辑地址与存储地址的转化就行

#include <iostream>
using namespace std;
void GetNext(int nextval[],string child){//求取nextval数组 
	nextval[0]=0;//子串第一个数的nextval数组值为0
	for(int i=1;i<child.length();i++){//求next串 i代表存储地址 而不是逻辑地址 
		int temp=i-1; //temp用于跟踪查找第一个和temp相匹配的字符(不能直接往前递归,因为子串的求取过程已经是对这个字符前的几个字符进行比较的过程) 
		while(child[i-1]!=child[nextval[temp]-1]&&temp!=0) temp=nextval[temp]-1;//nextval是逻辑地址 temp是存储地址 
		nextval[i]=nextval[temp]+1;
	}
//  nextval[0]=0;//子串第一个数的nextval数组值为0,(使用此方法时可以将上面的内容注释掉)
//	int i=1,j=0;
//	while(i<child.length()){ //求next串 
//		if(j==0||child[i-1]==child[j-1]) nextval[i++]=++j;
//		else j=nextval[j-1];
//	} 
	for(int i=1;i<child.length();i++)//next串升级成nextval串
		if(child[i]==child[nextval[i]-1]) nextval[i]=nextval[nextval[i]-1];
}
string KMP(string father,string child){
	int *nextval=new int[child.length()];//nextval数组的长度为子串长度 
	GetNext(nextval,child);//获取子串的nextval数组
	int i=0,j=0;//这里的i和j都是存储地址 并非逻辑地址 用于跟踪子串与父串 
	while(j<father.length()){//当父串没到末尾 就一直执行 
		if(i==-1||child[i]==father[j]){i++;j++;}//如果子串与父串指向的字符相同,就各往后移动一位 如果i=-1说明遇到了第一位与父串第一位不相等的情况 各后移一位 
		else i=nextval[i]-1; //i指向他的next数组的位置(根据next数组的定义,在子串中小于nextval[i]的字符均能与j之前的那些字符对应,直接比较i和j指向的位置就行了 
		if(i==child.length()) return "YES";//i到子串末尾说明子串读完了 即子串能在父串中找到 
	}
	return "NO";//如果最后都没有让i走到末尾 说明i不是j的子串 
}
int main(){
	string child;//存储子串 
	string father;//存储父串 
	while(cin>>father>>child&&father!="0"&&child!="0"){ //当字母串都只有0的时候结束 
		cout<<KMP(father,child)<<endl;//一次用KMP算法运算一行 
	}
	return 0;
} 

求取next数组的算法我用了两种方式,第一种比较好理解,注释掉的比较精简,比较妙,next转nextval的算法在求取next算法后面两行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘下来邦我吧

头发加了一根

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值