• KMP 算法


    function totalStrStr(haystack, needle) {
            const n = haystack.length;
            const m = needle.length;
            if (m === 0) {
                return 0;
            }
            const pi = new Array(m).fill(0);
    
            /**
             * 下面开始求解 needle 的 pi(前缀函数)
             **/
    
            // i=0 的时候, 字符串长度为1,则没有真前后缀,所以 pi[0] = 0,i 直接从1开始求 pi
            // 这里 j 初始化为 pi[i-1] 的值,也就是上一轮pi的值,因为 i 从0开始,所以初始化为上一轮的 pi值: 0
            for (let i = 1, j = 0; i < m; i++) {
                // 此处的步骤可以省略,因为每轮遍历后,j 保留着每轮的 pi 值,在下一轮遍历中 j 就已经是 pi[i-1] 了
                // j=pi[i-1];
    
                // 前缀函数的重要性质:求解第 i+1 个字符的 pi值,只依赖两个参数 j 、 pi[0:j-1]
                // 换个说法就是,只依赖上一轮的 pi值、needle 的前面的子串 needle[0:j-1] 的前缀函数
                while (j > 0 && needle[i] !== needle[j]) {
                    j = pi[j - 1];
                }
    
                // 注意这里,如果不等与的话,则有 pi[i]=0,但是 pi 中每个项初始化就为0,于是省略了这一步
                if (needle[i] === needle[j]) {
                    pi[i] = ++j;
                }
            }
            /**
             * 下面开始寻找 needle ,这个利用了前缀函数的性质,将 haystack 和 needle 想象成一个整体的字符串 totalStr:
             *      totalStr = needle + '$' + haystack;    // 注意'$'只是一个假想的字符,扮演分隔符的作用,在两个字符串中都不存在,作用是分隔 haystack 和 needle
             * 
             * 和上面求解 needle 的 pi 一样,设 totalStr 的前缀函数为 pi,j 为第 i 轮的上一轮的pi值,也就是 j=p[i-1]
             * 当 i<m 时,也就是等同于求解 needle 的 pi,这个已经在上面求解 needle 的 pi 的时候已经求出
             * 当 i=m 时,也就是 totalStr[i]='$' 的时候,必定有 pi[m]=0,因为分隔符在前面的字符中找不到,这也就是分隔符的作用,使得 j 重置为0,也避免真前缀、真后缀相互重叠的情况
             * 当 i>m 时,如果 pi[i] = m,这意味着什么呢?这意味着真前缀等于 needle,而真后缀就在 totalStr 的分隔符后半部分的字符串中,也就是在 haystack 找到了我们想要的字符串
             * 
             * 注意前缀函数的性质,当 i>m 时,我们并不需要保存totalStr[i] 的 pi值,因为 totalStr[i] 的求解只依赖两个参数:上一轮的 pi值 j、前缀函数 pi[0:j-1]
             * 而我们只需要找到pi[i]=m(i>m)的情况,也就是只需要前缀函数 pi[0:m-1],也就是 needle 的前缀函数
             **/
    
            // 此处,j 由于分隔符号的作用,已经重置为0。
            // 而遍历参数 i,如果以 totalStr 为基础,那么i初始化值为分隔符的下一位也就是 m+1,且 i<m+n+1
            // 但是此处的 i 以 haystack 为基础,分隔符的下一位就是 haystack 的第一位,也就是 0,所以 i=0,且 i<n
            for (let i = 0, j = 0; i < n; i++) {
                while (j > 0 && haystack[i] !== needle[j]) {
                    j = pi[j - 1]
                }
                if (haystack[i] === needle[j]) {
                    j++;
                }
                // j=m,意味着找到值为 needle 的真前缀(真后缀),计算其在 haystack 的位置直接返回
                if (j === m) {
                    return i - j + 1
                }
            }
            return -1;
        };
    
  • 相关阅读:
    [LeetCode] Minimum Depth of Binary Tree 二叉树最小深度
    [LeetCode] Intersection of Two Linked Lists 两链表是否相交
    [LeetCode] Permutations II 排列
    [LeetCode] Maximum Product Subarray 连续数列最大积
    Ncut matlab 代码bug 修复
    [LeetCode] Jump Game II 贪心
    【poj2182】【poj2828】树状数组/线段树经典模型:逆序查找-空位插入法
    【hdu1828/poj1177】线段树求矩形周长并
    【hdu1255】线段树求矩形面积交
    【hdu1542】线段树求矩形面积并
  • 原文地址:https://www.cnblogs.com/panshaojun/p/16356774.html
Copyright © 2020-2023  润新知