C++ BF和KMP算法

搞开发已经搞了几年了,回过头去看算法,还是在KMP这个算法上死了不少脑细胞。这个KMP算法和LZW压缩算法的思想有异曲同工之处,就是用已有的东西去推理出尽可能多的结论,这种算法往往不是特别好理解,因为有推理就会有证明,还有特殊情况或者优化,在这种变化下就会感到非常难受


KMP算法简而言之就是尽可能的减少BF中回溯的长度,从而减少不必要的执行,原理上来说就是A!=B,B!=C,=>A!=C这应该是最初提出这种算法的思想,这是能够减少回溯长度的最基本原理


以下代码中,Find_BF和Find_KMP应该是很容易看懂的,Find_KMP就是依据next数组对Find_BF的改进,而KMP算法中,最头疼的就是next数组的计算


首先借用别人画的一张图


首先,next数组的作用是,在不匹配时,j应该指向target的何处,根据图可以看出,若后缀与前缀有一部分相同,则可以省去前缀部分的回溯。若[0~K]是前缀,那么next[j]=K+1即可,那么重新整理思路,就是要在target中找出在各个位置不匹配时j的值,这个问题要仔细想想,如何在一个字符串中找出首尾相同的部分,前缀和后缀的长度是未知的,前缀后缀的长度该怎么确定?这就是计算next的一个核心思想,从前推到后,和LZW思想很相似,一个一个的向后推,next数组中记录的就是每个位置不匹配时该指向的位置


另外优化部分是由于比如target="aaaaaaa.b"这类大量重复字符下提出的,如果不优化,next就是-1 0 1 2 3 4.....然后1 2 3 4 他的字符都是a,此时回溯的效率非常低,因为这么多个a都要和已知的不相同的字符比较,由于字符是相同的,那么可以判定接下来的比较是不可能相等的,那么我在构造next时,判断如果字符相等,则用上一个位置中next存储的值。由于next构造是从前往后推出来的,所以可一次性回退,避免不必要的重复比较


#include<iostream>
using namespace std;

int Find_BF(char* source,char* target){
	int j=0; 
	int t_len=strlen(target);
	int s_len=strlen(source);
	for(int i=0;i<s_len;i++){

		if(source[i]==target[j]){
			j++;//记录相同的长度,若长度与target相同,则表示已完全匹配 
		}
		else if(j!=0){//回溯
			i-=j;//将i回溯至source中target的第一个元素的位置(最近的),i++ 后将移向下一个位置进行比较 
			j=0;//将j归0,寻找source中下一个与target第一个元素相等的位置
		}
		
		
		if(j==t_len){
			return i-(j-1);//i为在source中target最后一个元素的位置, i-(j-1) 表示找到的第一个元素的位置 
		}
	}
	return 0;
}
void GetNext(char* target,int*& next){
	int t_len=strlen(target);
	next=new int[t_len+1];
	next[0]=-1;
	int k=-1;
	
	for(int i=0;i<t_len;){

		if(k==-1||target[i]==target[k]){
			
			++i;
			++k;
			if(target[k]==target[i]){//优化增添 
				next[i]=next[k];//优化增添 
			} //优化增添 
			else{//优化增添 
				next[i]=k;
			}//优化增添 
			
		}
		else{
			k=next[k];
		}
	}
}



int Find_KMP(char* source,char* target,int* next){
	
	int j=0; 
	int t_len=strlen(target);
	int s_len=strlen(source);


	for(int i=0;i<s_len;){

		
		if(j==-1){//表示source[i]与target[0]不相同,右移i,进行下次比较 
			i++;
			j=0; 
		}
		
		
		if(source[i]==target[j]){
			i++;//同时右移,进行下一次比较 
			j++;//记录相同的长度,若长度与target相同,则表示已完全匹配 
		}
		else{//回溯 
			j=next[j];
		}
	

		if(j==t_len){
			return i-1-(j-1);
		}
	}
	
	return 0;
}

int main(){

	char* S="asdadsadasdasavvbbcaababcasdcsadab";
	char* T="ababc";
	

	cout<<Find_BF(S,T)<<endl;
	
	int* next;
	GetNext(T,next); 
	
	cout<<Find_KMP(S,T,next)<<endl;
	

	
	delete next;
	return 0;
}



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值