KMP算法详解

KMP算法是一种用于高效字符串匹配的算法,通过使用NEXT数组避免了暴力匹配中的冗余比较。BF算法(暴力法)是最基础的匹配方法,逐个字符比较。NEXT数组记录了字符串的前后缀关系,帮助在匹配失败时快速定位。KMP算法利用NEXT数组在不匹配时直接跳到合适位置继续比较,提高了效率。
摘要由CSDN通过智能技术生成

目录

一.KMP使用方向:

二.一般暴力法(BF算法):

 三.NEXT数组:

四.KMP写法:


一.KMP使用方向:

        KMP算法主要用于快速确定目标字符串是否存在于样板字符字符串中。相较于暴力匹配法,KMP引入了next数组,记录应该移动到的最合适位置。

二.一般暴力法(BF算法):

        将目标串从第一位逐次向后移动,和样本串相对应逐字进行比较。

        如样板串:abcdefgh            目标串:fgh

        首先和样板串前三个字母abc对比,发现对不上;样板串往后退一位,和bcd进行对比,又没有对上;按这个方式对比到fgh时,发现对比上了,因此目标串存在。

        在看一个演示代码:

string T="思念豫乡的明月,所以叫豫乡明月";
string P="豫乡明月";

int BF(string tString,string pString)
{
    int i=0; //主串中的位置

    int j=0; // 模式串中的位置

    for(;i<=tString.size()-pString.size();i++)
    {
        bool isPatt= true;

        for(;j<pString.size();j++)
        {
            if(tString[i+j]!=pString[j])
            {
                isPatt= false;

                break;
                //一旦不匹配,主串位置后退到i,模式串位置j回退到0;
            }
            // 当两个字符相同,就比较下一个
        }
        if(isPatt) return i;
    }
    return -1;
}
int main()
{
    cout<<BF(T,P);
}

 

 三.NEXT数组:

        如果不使用逐一配对的方法,就要思考如何一次移动更多位次。我们就要前缀和后缀的关系。如果部分前后缀相等,那么,如果后缀后的一个元素与样板串不匹配,那么可以直接将与后缀相同的前缀放到后缀的位置,从前缀后第一个元素开始比对。

        前缀:从第一个元素开始,往后若干连续元素

        后缀:从最后一个元素开始,往前若干连续元素

        样本串:aabaabaabaaf 目标串:aabaaf关于前后缀:

        a                    0(只有一个元素)

        aa                  1(前缀a,后缀a,相同数目为1)

        aab                0(第一个元素和最后一个元素不同)

        aaba              1

        aabaa            2(前缀aa,后缀aa)

        aabaaf          0

        首先和样本串开头进行匹配aabaa,然后下一个元素 b ,匹配不上,开始查找aabaa前缀和,前缀和是2,所以让前缀和后的元素和b 进行比对,发现可以配对上。

        那如何确定跳到哪里呢,这时要引入next数组。

        (1)next数组针对的是目标串

        (2)next[10]的含义是针对s[0]~s[10]这11个元素构成的字符串的相同前后缀长度-1(减一的目的是下标从0开始)。

        看以下next数组的定义方法:

void Next(string p){
	int plen = p.size();
	gnext[0] = -1;       //起始状态设置为-1,因为数组下标从0开始 
	int k = -1;			//起始时将k,j的差设置为1 
	int q =  0;
	while(q < plen - 1){
		if(k == -1 || p[q] == p[k]){  //k = -1 表示刚已经到达前缀的起始点 
			++k;
			++q;
			gnext[q] = k;  //找相同前后缀 
		}
		else{
			k = gnext[k];  //不匹配就回到最前面 
		}
	}
}

开始k是-1的原因是保证++k的第一个元素是0。

四.KMP写法:

将逐个后退改为前缀移动到后缀位置。

有以下写法:

int KMP(string s,string p){
	int slen = s.size();
	int plen = p.size();
	while(i<slen && j < plen){
		if(j == -1 || s[i] == p[j]){
			i++;
			j++;
		}
		else{
			j = gnext[j];  // 匹配不成功,就将j回到上一个该序列的前面 
		}
//		cout<<"i = "<<i<<"  j = "<<j<<endl; 
	}
	if(j = plen)
			return plen;
		
	else 
		return i;
}

 

 

 完整代码:

#include<iostream>
using namespace std;


typedef long long ll;
const ll N = 1e5 +5;
ll ans = 0;

string T ;
string P ;
int i = 0,j = 0;
int gnext[N];

void write(int x){
	if(x<0){
		putchar('-');
		x = -x;
	}
	if(x>9){
		write(x/10);
		putchar(x%10 + '0');
	}
	else putchar(x + '0');
	return;
}

void Next(string p){
	int plen = p.size();
	gnext[0] = -1;       //起始状态设置为-1,因为数组下标从0开始 
	int k = -1;			//起始时将k,j的差设置为1 
	int q =  0;
	while(q < plen - 1){
		if(k == -1 || p[q] == p[k]){  //k = -1 表示刚已经到达前缀的起始点 
			++k;
			++q;
			gnext[q] = k;  //找相同前后缀 
		}
		else{
			k = gnext[k];  //不匹配就回到最前面 
		}
	}
}

int KMP(string s,string p){
	int slen = s.size();
	int plen = p.size();
	while(i<slen && j < plen){
		if(j == -1 || s[i] == p[j]){
			i++;
			j++;
			if(ans<j) ans = j;  //记录最长前缀能符合多长
		}
		else{
			j = gnext[j];  // 匹配不成功,就将j回到上一个该序列的前面 
		}
//		cout<<"i = "<<i<<"  j = "<<j<<endl; 
	}
	if(j = plen)
			return plen;
		
	else 
		return i;
}

int main(){
	cin>>T>>P;
	Next(P);
	KMP(T,P);
//	write(KMP(T,P));
//	puts("");
	write(ans);
} 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱码天天

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值