关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 算法设计 »KMP算法的Next数组详解

    KMP算法的Next数组详解

    2015-03-05 00:00:00 出处:知致智之
    分享

    KMP的next数组求法是很不容易搞清楚的一部分,也是最重要的一部分。我该文就以我自己的感悟来慢慢推导一下吧!保证你看完过后是知其然,也知其所以然。

    假如你还不知道KMP是什么,请先阅读该文,先搞懂KMP是要干什么。

    下面我们就来说说KMP的next数组求法。

    KMP的next数组简单来说,假设有两个字符串,一个是待匹配的字符串strText,一个是要查找的关键字strKey。现在大家要在strText中去查找是否包含strKey,用i来表示strText遍历到了哪个字符,用j来表示strKey匹配到了哪个字符。

    假如是暴力的查找方法,当strText[i]和strKey[j]匹配失败的时候,i和j都要回退,然后从i-j的下一个字符开始重新匹配。

    而KMP就是保证i永远不回退,只回退j来使得匹配效率有所提升。它用的方法就是利用strKey在失配的j为之前的成功匹配的子串的特征来寻找j应该回退的位置。而这个子串的特征就是前后缀的相同程度。

    所以next数组其实就是查找strKey中每一位前面的子串的前后缀有多少位匹配,从而决定j失配时应该回退到哪个位置。

    我知道上面那段废话很难懂,下面我们看一个彩图:

    这个图画的就是strKey这个要查找的关键字字符串。假设我们有一个空的next数组,我们的工作就是要在这个next数组中填值。

    下面我们用数学归纳法来解决这个填值的问题。

    这里我们借鉴数学归纳法的三个步骤(或者说是动态规划?):

    1、初始状态
    2、假设第j位以及第j位之前的我们都填完了
    3、推论第j+1位该怎么填

    初始状态我们稍后再说,我们这里直接假设第j位以及第j位之前的我们都填完了。也就是说,从上图来看,我们有如下已知条件:
    next[j] == k;
    next[k] == 绿色色块所在的索引;
    next[绿色色块所在的索引] == 黄色色块所在的索引;

    这里要做一个说明:图上的色块大小是一样的(没骗我?好吧,请忽略色块大小,色块只是代表数组中的一位)。

    我们来看下面一个图,可以得到更多的信息:

    1.由”next[j] == k;”这个条件,我们可以得到A1子串 == A2子串(根据next数组的定义,前后缀那个)。

    2.由”next[k] == 绿色色块所在的索引;”这个条件,我们可以得到B1子串 == B2子串。

    3.由”next[绿色色块所在的索引] == 黄色色块所在的索引;”这个条件,我们可以得到C1子串 == C2子串。

    4.由1和2(A1 == A2,B1 == B2)可以得到B1 == B2 == B3。

    5.由2和3(B1 == B2, C1 == C2)可以得到C1 == C2 == C3。

    6.B2 == B3可以得到C3 == C4 == C1 == C2

    上面这个就是很简单的几何数学,仔细看看都能看懂的。我这里用相同颜色的线段表示完全相同的子数组,方便观察。

    接下来,我们开始用上面得到的条件来推导假如第j+1位失配时,我们应该填写next[j+1]为多少?

    next[j+1]即是找strKey从0到j这个子串的最大前后缀:

    #:(#:在这里是个标记,后面会用)我们已知A1 == A2,那么A1和A2分别往后增加一个字符后是否还相等呢?我们得分情况讨论:

    (1)假如str[k] == str[j],很明显,我们的next[j+1]就直接等于k+1。

    用代码来写就是next[++j] = ++k;

    (2)假如str[k] != str[j],那么我们只能从已知的,除了A1,A2之外,最长的B1,B3这个前后缀来做文章了。

    那么B1和B3分别往后增加一个字符后是否还相等呢?

    由于next[k] == 绿色色块所在的索引,我们先让k = next[k],把k挪到绿色色块的位置,这样我们就可以递归调用”#:”标记处的逻辑了。

    由于j+1位之前的next数组我们都是假设已经求出来了的,因此,上面这个递归总会结束,从而得到next[j+1]的值。

    我们唯一欠缺的就是初始条件了:

    next[0] = -1, k = -1, j = 0

    另外有个特殊情况是k为-1时,不能继续递归了,此时next[j+1]应该等于0,即把j回退到首位。

    即 next[j+1] = 0; 也可以写成next[++j] = ++k;

    public static int[] getNext(String ps)
    {
        char[] strKey = ps.toCharArray();
        int[] next = new int[strKey.length];
    
        // 初始条件
        int j = 0;
        int k = -1;
        next[0] = -1;
    
        // 根据已知的前j位推测第j+1位
        while (j < strKey.length - 1)
        {
            if (k == -1 || strKey[j] == strKey[k])
            {
                next[++j] = ++k;
            }
            else
            {
                k = next[k];
            }
        }
    
         return next;
    }

    现在再看这段代码应该没有任何问题了吧。

    优化:

    细心的朋友应该发现了,上面有这样一句话:

    (1)假如str[k] == str[j],很明显,我们的next[j+1]就直接等于k+1。用代码来写就是next[++j] = ++k;

    可是我们知道,第j+1位是失配了的,假如我们回退j后,发现新的j(也就是此时的++k那位)跟回退之前的j也相等的话,必然也是失配。所以还得继续往前回退。

    public static int[] getNext(String ps)
    {
        char[] strKey = ps.toCharArray();
        int[] next = new int[strKey.length];
    
        // 初始条件
        int j = 0;
        int k = -1;
        next[0] = -1;
    
        // 根据已知的前j位推测第j+1位
        while (j < strKey.length - 1)
        {
            if (k == -1 || strKey[j] == strKey[k])
            {
                // 假如str[j + 1] == str[k + 1],回退后仍然失配,所以要继续回退
                if (str[j + 1] == str[k + 1])
                {
                    k = next[k + 1];
                    next[++j] = k;
                }
                else
                {
                    k = k + 1;
                    next[++j] = k;
                }
            }
            else
            {
                k = next[k];
            }
        }
    
         return next;
    }

    好了,自此KMP的next求法全部讲解完毕。欢迎大家指出文章的错误,我好更加完善它。

    ———————————————————————————————————-

    下面说说面试的时候,给一个字符串,要你写出它的Next数组,应该怎么写:

    ①:先对每一位左边的子串求出最大前后缀串的长度,作为初始的Next数组

    ②:因为第一位失配时需要移动i,因此赋值为-1

    ③:P[3] == A, Next[3] == 0, P[0] == A; 所以P[3] == P[0], (移动过去后还是失配,需要继续移动),优化Next[3]为Next[0],即-1

    ④:同理优化Next[10]为Next[0],即-1

    ⑤:同理优化P[14],P[15],P[16]

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。