算法 实现substr() KMP算法

实现 strStr() 函数。

给你两个字符串 haystack needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1

示例 1:

输入:haystack = "hello", needle = "ll"
输出:2
示例 2:

输入:haystack = "aaaaa", needle = "bba"
输出:-1
示例 3:

输入:haystack = "", needle = ""
输出:0
 

提示:

0 <= haystack.length, needle.length <= 5 * 104
haystack 和 needle 仅由小写英文字符组成

用KMP算法的解决的是什么问题:

如果主串和模式串发生匹配冲突,通过前缀表也就是next数组,能够记录前面匹配过的一些内容,从而让模式串的指针不会从头开始匹配,从而提升效率。

知道下面的几个概念和KMP算法的核心,这个解法就理解了。后续会补充。

next数组【前缀表】是什么

next数组怎么求

前缀和后缀

最长相等前缀和后缀

看代码前,附上我看的视频链接,看了2-3遍视频,才能搞懂,我比较笨。

视频链接:

帮你把KMP算法学个通透!(求next数组代码篇)_哔哩哔哩_bilibili

帮你把KMP算法学个通透!(理论篇)_哔哩哔哩_bilibili

代码随想录网站:

代码随想录

  纯净无注释代码 再下面有 “注释版代码”

            var strStr = function (haystack, needle) {
            if (needle.length === 0)
                return 0;

            const getNext = (needle) => {
                let next = [];
                let j = 0;
                next.push(j);

                for (let i = 1; i < needle.length; ++i) {
                    while (j > 0 && needle[i] !== needle[j])
                        j = next[j - 1];
                    if (needle[i] === needle[j])
                        j++;
                    next.push(j);
                }

                return next;
            }

            let next = getNext(needle);
            let j = 0;
            for (let i = 0; i < haystack.length; ++i) {
                while (j > 0 && haystack[i] !== needle[j])
                    j = next[j - 1];
                if (haystack[i] === needle[j])
                    j++;
                if (j === needle.length)
                    return (i - needle.length + 1);
            }

            return -1;
        };

注释版本代码

        // 注释版本代码
        var strStr = function (haystack, needle) {
            if (needle.length === 0)
                return 0;
            const getNext = (needle) => {
                // 1. 初始化 next数组 用needle子串生成next数组
                let next = [];
                // 2. 初始化 j指针 
                // 求最长相等前缀和后缀的长度
                // j => 前缀末尾 / 也代表 i【包括i】之前的子串的最长相等前后缀的长度
                // i => 后缀末尾 for循环初始化
                let j = 0;
                // j初始化为0
                next.push(j);
                // next数组的每一位的值 都是记录当前字符之前的子串的最长相等前后缀的长度
                // a =》next[0]
                // aa =》next[0,1]
                // aab =》next[0,1,0] 此时最长先等前后缀长度是0 a和b不同
                // aaba =》next[0,1,0,1]
                // aabaa =》next[0,1,0,1,2]
                // aabaaa =》next[0,1,0,1,2,1]
                for (let i = 1; i < needle.length; ++i) {
                    while (j > 0 && needle[i] !== needle[j])
                        // 0. 这里用while循环是关键 因为j的回退不是只回退一次 而是很多次
                        // 1. j从0开始移动,i从1开始移动 
                        // 2. 一旦指针指的值不同 ,j要回退,回退到哪里是关键
                        // 3. next[j - 1]比如是0,j就回退到索引0的位置
                        // 4. next[j - 1]比如是1,j就回退到索引1的位置 因此如下赋值
                        j = next[j - 1];
                    if (needle[i] === needle[j])
                        // 5. 如果相同
                        // j++ i++ i是for循环自增 
                        j++;
                    next.push(j);
                    // 6. next.push(j) 必须放到末尾是关键 不管是冲突还是相等, 都应该往next数组里面push值
                }

                return next;
            }

            let next = getNext(needle);
            // 上面的next数组生成好了 比如是[0, 1, 0, 1, 2, 0]
            let j = 0;
            for (let i = 0; i < haystack.length; ++i) {
                // 这里是i和j意义不同
                // i遍历主串
                // j遍历模式串
                while (j > 0 && haystack[i] !== needle[j])
                    // 这里看似代码类似,但实际是匹配主串和模式串
                    // 如果不等就让j回退到一个位置,前一个j-1对应next数组的值,比如next[j - 1] 是 3 就回退到索引3
                    // 如果下面相等 就++
                    j = next[j - 1];
                if (haystack[i] === needle[j])
                    j++;
                // 如果j到头了,就返回
                if (j === needle.length)
                    return (i - needle.length + 1);
                    // 这里return i - needle.length + 1是我没想到的
                    // 但是仔细一对比 画个图就可以看出来了
            }

            return -1;
        };
        console.log(strStr('aabaabaaf', 'aabaaf')); // 3
        console.log(strStr('aabaabaafdaabaafg', 'aabaafg')); // 10

上面的代码也是从“代码随想录"摘的,但是注释是我自己加滴。

再次感谢carl大哥的讲解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Oracle数据库本身不直接提供MurmurHash3算法实现。但是,你可以通过编写自定义函数或过程来在Oracle中实现MurmurHash3算法。 以下是一个简单的示例,展示了如何在Oracle中实现MurmurHash3算法: ```sql CREATE OR REPLACE FUNCTION murmurhash3 (input VARCHAR2) RETURN NUMBER AS seed CONSTANT NUMBER := 0; -- 可根据需求设置种子值 c1 CONSTANT NUMBER := 0xcc9e2d51; c2 CONSTANT NUMBER := 0x1b873593; r1 CONSTANT NUMBER := 15; r2 CONSTANT NUMBER := 13; m CONSTANT NUMBER := 5; n CONSTANT NUMBER := 0xe6546b64; hash NUMBER := seed; len NUMBER := LENGTH(input); k NUMBER; begin FOR i IN 1..CEIL(len/4) LOOP k := ASCII(SUBSTR(input, (i-1)*4+1, 1)) + ASCII(SUBSTR(input, (i-1)*4+2, 1))*256 + ASCII(SUBSTR(input, (i-1)*4+3, 1))*65536 + ASCII(SUBSTR(input, (i-1)*4+4, 1))*16777216; k := BITAND(k * c1, 0xffffffff); k := (k << r1) OR (k >> (32 - r1)); k := BITAND(k * c2, 0xffffffff); hash := BITXOR(BITAND(hash XOR k, 0xffffffff), BITAND(BITXOR(k, n), 0xffffffff)); hash := (hash << r2) OR (hash >> (32 - r2)); hash := BITAND(hash * m, 0xffffffff); hash := hash + n; END LOOP; hash := BITAND(hash * 0x85ebca6b, 0xffffffff); hash := hash XOR BITAND(hash, 0xffff); hash := BITAND(hash * 0xc2b2ae35, 0xffffffff); hash := hash XOR BITAND(hash, 0xffff); RETURN hash; end; / ``` 使用以上代码,你可以在Oracle数据库中创建一个名为`murmurhash3`的自定义函数。你可以通过调用该函数并传入一个字符串参数来获取MurmurHash3算法的哈希值。 请注意,此示例中的MurmurHash3实现仅用于演示目的,并不能保证与其他平台上的MurmurHash3算法完全一致。对于生产环境中的使用,你可能需要根据自己的需求进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值