KMP模式匹配算法模板

本文深入讲解了KMP算法中next数组的求解方法,并通过代码示例展示了算法的实现过程。此外,还提出了一种针对特定场景的算法优化方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

next数组


kmp算法的关键在于next数组的求解,next的值仅取决于子串本身而和主串无关。我们可以从定义分析出发用一种递推思想求得next的函数值。这里以数组下标从0开始为例


这里又有一种将子串看成"主串"的思想,利用前面的next信息,递推构造后面的值

关键代码为:

while(j<len2-1){
        if(k==-1||t[j]==t[k])//如果k为-1,即到了递推前部边界,由下面的语句可得next[++j]=0
           next[++j]=++k;   //如果t[j]==t[k],则相当于递推地直接获得了j+1的"最大满足"k 
        else k=next[k];//递推,降低k看前后是否匹配
    }


代码示例

#include<bits/stdc++.h>
#define max_size 200
using namespace std;
char p[max_size];
char t[max_size];
int next[max_size+5];
int len1,len2,pos;
void get_next(char *t,int *next)
{
    int j=0;
    int k=-1;
    next[j]=k;
    while(j<len2-1){
        if(k==-1||t[j]==t[k]) next[++j]=++k;
        else k=next[k];
    }
}
int index_kmp(char *p,char *t,int *next)
{
    int i=pos-1;
    int j=-1;
    get_next(t,next);
    while(i<len1&&j<len2)
    {
        if(j==-1||p[i]==t[j]){
            ++i;
            ++j;
        }
        else j=next[j];
    }
    if(j>=len2) return i-len2+1;
    else return 0;
}
int main()
{
    cin>>p>>t>>pos;
    len1=strlen(p);
    len2=strlen(t);
    int ans=index_kmp(p,t,next);
    cout<<ans<<endl;
    return 0;
}

对于get_next函数来说,若t的长度是m,因只涉及到简单的单循环,其时间复杂度为O(m)。而由于i的值不回溯,使得index_kmp()算法效率得到了提高,其时间复杂度为O(n+m)。相较于朴素模式匹配算法的O((n-m+1)*m)来说,是要好一些。

需要强调的是,kmp算法仅当子串与主串之间存在许多“部分匹配”的情况下才能体现出它的优势,否则两者差异并不明显。


get_next函数的改进

有一类情况:例如主串为aaaabcde  子串为aaaaax ,在匹配的过程中会发现有些步骤是多余的。

因为对于主串和子串大面积相同的情况,next数组的移动并不明显,于是我们将代码改进

void get_next(char *t,int *next)
{
    int j=0;
    int k=-1;
    next[j]=k;
    while(j<len2-1){
        if(k==-1||t[j]==t[k]){
        	++j;
        	++k;
        	if(t[j]!=t[k])//若当前字符与前缀字符不同,则当前的k为next在j位置的值 (这部分同上) 
        		next[j]=k;
        	else //如果与前缀字符相同,则将前缀字符的next值直接赋给next在j位置的值(改进部分) 
		        next[j]=next[k];
        }
        else k=next[k];//如果与前缀字符相同, 
    }
}


e.g

 


总结

改进过的kmp算法,它是在计算出next值的同时,如果a位字符与它next值指向的b位字符相等,则该a位字符的next就指向b位的next值;如果不等,则该a位的next(改进)的值就是它自己a位的next(原)的值。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值