KMP算法的数学原理(优化版)

简介: KMP算法

对于一个有限自动机M,它是一个5元组(S,s₀,A,Σ,δ),S是有限状态集,s₀是初始状态(x₀∈X),A是可接受状态集(A⊆X),∑是有限输入表,δ是状态转移函数(从S×Σ到S的映射)。假定有一个模式串p="abaabcb"(长度m),待匹配字符串s="abaabaabcb"(长度n),当第5个字符'c'匹配失败时,寻常的做法是将p的索引回退到0,s的索引回退到1,再重新进行匹配。观察s与p得知:p0...4==s0...4,p0...1==p3...4=="ab",当s5与p5无法匹配时,可以尝试判断s5==p2是否成立,若成立,由前面的推论可知p0...1,2==s3...4,5,所以第5个字符匹配失败时,可以将p的索引回退到2继续进行比较,这样就无需变动s的索引,节约了计算时间,所以只要能够为状态机设计出合理的状态转移函数,就能够加速字符串的匹配。

更一般化情况下,对于模式串p0...m-1,待匹配字符串s0...n-1,对任意i∈0,m-1,j∈0,n-1,有:i,j=δ(i,pj) ( i 为状态机当前状态索引,j 为 s 的索引)。对于δ函数,当循环输入一个字符 pj 时有两种结果,即匹配成功和匹配失败。若匹配成功,i 向后移一位,继续与pj+1进行比较;若匹配失败,则需要将 i 进行跳转,原因后面会解释,这里令 i 的跳转表为 next0...m-1,每次跳转后需重新比较pi与sj,直到它们相等或者i==0时终止跳转,最后再进行一次比较,若相等则 i 可以向后移一位继续与 sj+1比较,伪代码如下:

delta(p,s,next,i,j)
    while i>0 and p[i]!=s[j]
        i=next[i]
    if p[i]==s[j]
        i=i+1
    return i
AI 代码解读
kmp_search(p,s,next)
    m=p.length
    n=s.length
    i,j=0
    while i<m and j<n
        i=delta(p,s,next,i,j)
        j=j+1
    if i==m
        return j-m
    return -1
AI 代码解读

前面的模式串p="abaabcb"在第5个字符匹配失败时,因为有p0...4==s0...4,p0...1==p3...4==ab,所以 i 可以回退到2继续进行匹配,这里的 "ab" 我称为p0...4和pk...5的最长公共前缀,其长度记为 π,满足:

π[i] = max{ k : p[0...k-1]==p[i-k...i-1] ∧ k < i }
AI 代码解读

由上式可推 πi+1=max{k:p0...k-1==p(i+1)-k...(i+1)-1∧k<(i+1)},π0=0,令 πi=x:

1)当pi==px时,总有 p0...x-1px==pi-x...i-1pi,即p0...(x+1)-1==p(i+1)-(x+1)...(i+1)-1,可得πi+1==x+1= =πi+1,因此,对任意pi==p[πi],满足递推式:πi+1==πi+1。

2)当pi !=px时,p0...x-1px==pi-x...i-1pi 显然不成立,那么有没有更短的长度为y(y<x)的公共前缀使 p0...y-1py ==pi-y...i-1 成立呢?这里我同样可以对 px 进行状态转移,令y=πx,由于y是x位置的最长公共前缀的长度,所以有 p0...y-1 ==px-y...x-1,又p0...y-1是p0...x-1的最长前缀,所以p0...y-1也是pi-x...i-1的最长前缀,因此满足:πi+1=πx。

从上面的结论来看,π数组跟next数组是有紧密联系的,它们都完成匹配过程中的状态转移,但是却有些细微的区别,不少网络平台上分享的KMP算法在我看来都是有瑕疵的。考虑这样一种情况,在 π 数组已经计算好的前提下,当pi!=sj,需要将 i 移至 πi,令 k=πi,若 pk==pi,那么再比较pk与sj是没有意义的,因此将这样的情况迭代优化后,就能得到 next 数组,满足:

公式.png
伪代码如下:

compute_next(p,next)
    next[0]=0
    k=0
    m=p.length
    for i = 1 to 
        if p[i]==p[k]
            next[i]=next[k]
            k=k+1
        else
            next[i]=k
            while k>0
                k=next[k]
                if(p[i]==p[k])
                    k=k+1
                    goto out
            <out>
AI 代码解读

分析伪代码不难得知该算法的时间复杂度是O(m+n),以下是C语言实现的KMP算法:

#include <string.h>

void compute_next(const char* p, int m, int next[]) {
   
    next[0] = 0;
    int k = 0;
    for (int i = 1; i < m; ++i) {
   
        if (p[i] == p[k]) {
   
            next[i] = next[k];
            ++k;
        } else {
   
            next[i] = k;
            while (k > 0) {
   
                k = next[k];
                if (p[i] == p[k]) {
   
                    ++k;
                    break;
                }
            }
        }
    }
}

int delta(const char* p, const char* s, int next[], int i, int j) {
   
    while (i > 0 && p[i] != s[j]) {
   
        i = next[i];
    }
    if (p[i] == s[j]) {
   
        ++i;
    }
    return i;
}

int kmp_search(const char* p, const char* s, int m, int n, int next[]) {
   
    int i = 0, j = 0;
    for (; i < m && j < n; ++j) {
   
        i = delta(p, s, next, i, j);
    }
    return i == m ? j - m : -1;
}
AI 代码解读

delta函数可以合并到kmp_search函数进行简化,如下:

void compute_next(const char* p, int m, int next[]) {
   ...}

int kmp_search(const char* p, const char* s, int m, int n, int next[]) {
   
    int i = 0, j = 0;
    for (; i < m && j < n; ++j) {
   
        while (i > 0 && p[i] != s[j]) {
   
            i = next[i];
        }
        if (p[i] == s[j]) {
   
            ++i;
        }
    }
    return i == m ? j - m : -1;
}
AI 代码解读

测试用例:

int main(int argc, char** argv) {
   
    const char* testStrings[][2] = {
   
        {
   "tencent", "encentencentabcskf"},      //true
        {
   "alibaba", "ajsdkalibalibabisk"},      //false
        {
   "baidu", "baibai.www.baidu.com"},      //true
        {
   "bytedance", "ajbytedadanceaaa"},      //false
        {
   "google","googoelglegooglegooo"},      //true
        {
   "microsoft","microsofmicrosofp"}       //false
        };
    int count = sizeof(testStrings) / sizeof(testStrings[0]);
    const char *p, *s;
    int m, n;
    for (int i = 0; i < count; ++i) {
   
        p = testStrings[i][0];
        s = testStrings[i][1];
        m = strlen(p);
        n = strlen(s);
        int next[m];
        compute_next(p, m, next);
        int ret = kmp_search(p, s, m, n, next);
        if (ret != -1)
            printf("模式串'%s'移 %d 位匹配'%s'成功\n", p, ret, s);
        else
            printf("模式串'%s'与'%s'匹配失败\n", p, s);
    }
    return 0;
}
AI 代码解读
目录
打赏
0
1
1
0
1
分享
相关文章
短视频到底如何推荐的?深度剖析视频算法推送原理详细且专业的解读-优雅草卓伊凡-【01】短视频算法推荐之数据收集
短视频到底如何推荐的?深度剖析视频算法推送原理详细且专业的解读-优雅草卓伊凡-【01】短视频算法推荐之数据收集
63 12
短视频到底如何推荐的?深度剖析视频算法推送原理详细且专业的解读-优雅草卓伊凡-【01】短视频算法推荐之数据收集
基于GA遗传优化TCN时间卷积神经网络时间序列预测算法matlab仿真
本内容介绍了一种基于遗传算法优化的时间卷积神经网络(TCN)用于时间序列预测的方法。算法运行于 Matlab2022a,完整程序无水印,附带核心代码、中文注释及操作视频。TCN通过因果卷积层与残差连接学习时间序列复杂特征,但其性能依赖超参数设置。遗传算法通过对种群迭代优化,确定最佳超参数组合,提升预测精度。此方法适用于金融、气象等领域,实现更准确可靠的未来趋势预测。
基于BBO生物地理优化的三维路径规划算法MATLAB仿真
本程序基于BBO生物地理优化算法,实现三维空间路径规划的MATLAB仿真(测试版本:MATLAB2022A)。通过起点与终点坐标输入,算法可生成避障最优路径,并输出优化收敛曲线。BBO算法将路径视为栖息地,利用迁移和变异操作迭代寻优。适应度函数综合路径长度与障碍物距离,确保路径最短且安全。程序运行结果完整、无水印,适用于科研与教学场景。
基于NSGAII的的柔性作业调度优化算法MATLAB仿真,仿真输出甘特图
本程序基于NSGA-II算法实现柔性作业调度优化,适用于多目标优化场景(如最小化完工时间、延期、机器负载及能耗)。核心代码完成任务分配与甘特图绘制,支持MATLAB 2022A运行。算法通过初始化种群、遗传操作和选择策略迭代优化调度方案,最终输出包含完工时间、延期、机器负载和能耗等关键指标的可视化结果,为制造业生产计划提供科学依据。
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
基于GA遗传优化TCN-LSTM时间卷积神经网络时间序列预测算法matlab仿真
本项目基于MATLAB 2022a实现了一种结合遗传算法(GA)优化的时间卷积神经网络(TCN)时间序列预测算法。通过GA全局搜索能力优化TCN超参数(如卷积核大小、层数等),显著提升模型性能,优于传统GA遗传优化TCN方法。项目提供完整代码(含详细中文注释)及操作视频,运行后无水印效果预览。 核心内容包括:1) 时间序列预测理论概述;2) TCN结构(因果卷积层与残差连接);3) GA优化流程(染色体编码、适应度评估等)。最终模型在金融、气象等领域具备广泛应用价值,可实现更精准可靠的预测结果。
基于WOA鲸鱼优化的CNN-LSTM-SAM网络时间序列回归预测算法matlab仿真
本内容介绍了一种基于CNN-LSTM-SAM网络与鲸鱼优化算法(WOA)的时间序列预测方法。算法运行于Matlab2022a,完整程序无水印并附带中文注释及操作视频。核心流程包括数据归一化、种群初始化、适应度计算及参数更新,最终输出最优网络参数完成预测。CNN层提取局部特征,LSTM层捕捉长期依赖关系,自注意力机制聚焦全局特性,全连接层整合特征输出结果,适用于复杂非线性时间序列预测任务。
基于免疫算法的最优物流仓储点选址方案MATLAB仿真
本程序基于免疫算法实现物流仓储点选址优化,并通过MATLAB 2022A仿真展示结果。核心代码包括收敛曲线绘制、最优派送路线规划及可视化。算法模拟生物免疫系统,通过多样性生成、亲和力评价、选择、克隆、变异和抑制机制,高效搜索最优解。解决了物流仓储点选址这一复杂多目标优化问题,显著提升物流效率与服务质量。附完整无水印运行结果图示。
基于免疫算法的最优物流仓储点选址方案MATLAB仿真
基于生物地理算法的MLP多层感知机优化matlab仿真
本程序基于生物地理算法(BBO)优化MLP多层感知机,通过MATLAB2022A实现随机数据点的趋势预测,并输出优化收敛曲线。BBO模拟物种在地理空间上的迁移、竞争与适应过程,以优化MLP的权重和偏置参数,提升预测性能。完整程序无水印,适用于机器学习和数据预测任务。
107 31
基于LSB最低有效位的音频水印嵌入提取算法FPGA实现,包含testbench和MATLAB对比
本项目展示了一种基于FPGA的音频水印算法,采用LSB(最低有效位)技术实现版权保护与数据追踪功能。使用Vivado2019.2和Matlab2022a开发,完整代码含中文注释及操作视频。算法通过修改音频采样点的最低有效位嵌入水印,人耳难以察觉变化。然而,面对滤波或压缩等攻击时,水印提取可能受影响。该项目运行效果无水印干扰,适合实时应用场景,核心逻辑简单高效,时间复杂度低。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等