LeetCode | C++ 28. 实现 strStr()、459.重复的子字符串

52 篇文章 0 订阅


初次接触kmp算法,确实不太好理解,后面还需要多看几次。

(1)28. 实现 strStr()

LeetCode-28-题目链接

  • 该题目难点在于 如何构建next 前缀表的理论解释,next数据是字符串中各个子串的一个最长相等前后缀的集合,遇到了冲突,next数组需要向前回退,这是next数组的核心所在,在计算next数组时有一种递归的感觉。
  • 还是需要多分析下,回退这种方式,如果遇到前后缀不相等的,便会回退,直到 要么回退到两者有相等的在开始,要么就是起始位置。
  • 还有觉得卡哥的给定最长相等前后缀的定义更为准确,公共代表是同一个东西,相等就不一定了。
  • 还有的一些解法,会把next数组的值整体往右移一位,next[0] = -1, 是当遇到冲突是,就不用往前找了,直接就是冲突位置对应的next值。
class Solution {
public:
    int strStr(string haystack, string needle) {
        int next[needle.size()];
        getNext(needle, next);

        for (int i = 0; i < needle.size(); i++) {
            std::cout << next[i] << std::endl;
        }
        int j = 0;

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

    // 先去求模板串对应的前缀表
    void getNext(string s, int* next) {
        // j:代表前缀末尾,同时也是前缀后缀最长相等的长度
        // 注:j 的前缀末尾应该是 最长相等的前缀末尾
        // i: 代笔后缀末尾;
        int j, i;
        //初始时
        j = 0;
        next[0] = 0;
        for (int i = 1; i < s.size(); i++) {
            while (j > 0 && s[j] != s[i]) {
                j = next[j - 1];
                //j = 0;
            }
            if (s[j] == s[i]) {
                j++;
            }
            next[i] = j;
        }
    }
};

(2)459.重复的子字符串

LeetCode-459-题目链接

  • 对于暴力法,取子串用一层for循环即可,是因为默认的子串一定是从最前面的这个元素开始的,如果开头的元素你都不包含的话,你所搜索的子串,它能否去组成这个整个字符串其实都没有意义。

移动窗口法

  • 若一个字符串能够由一个子串重复构成,那么将该字符串 两个加起来,掐头去尾,一定可以找到原始字符串。
  • 原因在于任何一个由重复字符子串组成的字符串,它的前半部分和后半部分一定是相等的,注:不一定是从正中间劈开的, 例如:ababab 可以是 abab abab。
class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        string s2 = s + s;
        s2.erase(s2.begin());
        s2.erase(s2.end() - 1);
        if (s2.find(s) != std::string::npos) return true; 
        return false;      
    }  
};

kmp法

  • 如果一个字符串是由重复子串组成的,那么它的最小重复单位就是它的最长相等前后缀不包含的那个子串。可以通过数学递推证明。
  • 数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。 如果不能整除的话,说明不是,例如:aabaaba,不是, 最长相等前后缀:aaba, 剩下的为 aab。
class Solution {
public:
    bool repeatedSubstringPattern(string s) {
        // kmp算法
        int next[s.size()];
        getNext(s, next);
        int delta = s.size() - next[s.size() - 1];
        if (delta != s.size() && s.size() % delta == 0) {
            return true;
        } else {
            return false;
        }
    }
    // 先去求模板串对应的前缀表
    void getNext(string s, int* next) {
        // j:代表前缀末尾,同时也是前缀后缀最长相等的长度
        // 注:j 的前缀末尾应该是 最长相等的前缀末尾
        // i: 代笔后缀末尾;
        int j, i;
        //初始时
        j = 0;
        next[0] = 0;
        for (int i = 1; i < s.size(); i++) {
            while (j > 0 && s[j] != s[i]) {
                j = next[j - 1];
                //j = 0;
            }
            if (s[j] == s[i]) {
                j++;
            }
            next[i] = j;
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值