代码随想录算法训练营day9

4 阅读3分钟

使用kmp算法来寻找字符串 1.设计和改进next的数组是这个问题最重要的核心。初学者经常被公共真前缀和真后缀这两个概念搞得晕头转向。如果无法理解这两个概念,强烈建议去看看王道或者邓俊辉数据结构kmp算法的讲解,动笔写写画画可以快速掌握手动构造next数组的方法。但是,代码构建和算法理解又是两个概念,尤其还涉及到改进数组的概念 2.设计next数组的方法依然是我们的老方法,双指针法。我们用一个慢指针作为文本串指针,用一个快指针作为模式串的指针,慢指针从不回退,快指针一旦不匹配条件即快速回退至匹配处。还要掌握迭代的概念,在next数组的设计中,我们需要理解next[0]为什么被赋值成为-1,这实际上是采用一种哨兵结点的方式,虚拟模式串头部存在一个永久匹配的字符,这样及可以做到快速迭代,减少逻辑判断。而我们通过迭代的思想,在掌握next[i]时,如何求得next[i+1],区分两种情况,如果字符匹配,next[i+1]++,不匹配则快速回退至最近匹配处,t=next[i]重新匹配。 3.改进next数组的逻辑判断,对于000001的next数组的设计可以发现,next数组退化成暴力算法,这里就是因为没有吸收不匹配字符的教训情况,我们需要把这个教训利用起来。因此,next数组在构建的过程中,自然知道已构建的字符串信息,所以当出现相同的匹配字符时,应当及时避免再次使用相同字符再次进行匹配。

public:
    int strStr(string haystack, string needle) {
        int n=haystack.size();//记录文本串长度
        int m=needle.size();//记录模式串长度
        //排除意外情况
        // 1. 修正边界条件:只有needle为空时返回0,其他无效情况返回-1
        if (m == 0) return 0;     // 模式串为空,按题目要求返回0
        if (n < m) return -1;     // 文本串比模式串短,不可能匹配

        //构造next数组
        int i=0;
        int* next=new int[m];
        int t=next[0]=-1;//注意,t不仅相当于慢指针,也是next数组中的值
        //已知next[i]求next[i+1]
        while(i<m-1){
            if(t<0||needle[t]==needle[i]){
                t++;
                i++;
                //改进next数组,通过对比是否为原来相同字符来吸取教训,例如00001情况,防止退化成暴力算法
                 next[i] = (needle[i] != needle[t]) ? t : next[t];
                }
                else
                t=next[t];
        }
        //启用next表对文本串进行匹配
         int j = 0;  // 文本串指针
        int p = 0;  // 模式串指针(重新定义,避免和构造next的i冲突)
        while (j < n && p < m) {
            // 修正条件:p<0代表模式串指针回退到起点,直接匹配下一个字符
            if (p < 0 || haystack[j] == needle[p]) {
                j++;
                p++;
            } else {
                // 不匹配时,模式串指针回退(利用next数组)
                p = next[p];
            }
        }

        // 释放动态数组
        delete[] next;

        // 4. 修正返回值:匹配成功返回起始索引,失败返回-1
        if (p == m) {
            return j - m;
        } else {
            return -1;
        }
    }
};