代码随想录算法训练营第九天 | 459.重复的子字符串

55 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

459.重复的子字符串

题目分析

题目要求给定一个非空的字符串检查,是否可以通过它的一个子串重复多次构成这个非空字符串,也就是这个字符串是否是由重复子串构成的。

解法一 移动匹配

如果一个字符串由重复字串组成,那么这个字符串的子串至少有两个。那么将这个字符串两个相加,那么合成的字符串中子串至少有4个。也就是合成的字符串中中间会有一个原来的字符串,这样就可以通过判断合成的字符串中是否有一个原来的字符串来判断题目。

const repeatedSubstringPattern = s => (s + s).slice(1, s.length * 2 - 1).indexOf(s) !== -1;

通过去除合成的字符串的首尾字母来防止遍历到的是原来的字符串,使用内置函数后甚至一行代码就可以解决问题。

这样做看起来很简单,代码很简洁,但是使用了内置函数内置函数的复杂度有时候也很高。这道题又涉及到了查找字符串,我们上一节说的查找字符串是KMP的拿手本领。

解法二 KMP

在最长相等前后缀中没有出现的子串,就是最小重复子串。因为最长前缀是不包含最后一个字符的其他字符,而最长后缀是不包含第一个字符的其他字符,这样第一次子串不能前后缀相等,由此可以得出结论。

image.png

代码如下:

var repeatedSubstringPattern = function (s) {
    if (s.length === 0) return false;
    const getNext = (s) => {
        let next = [];
        let j = 0;
        next.push(j);
        for (let i = 1; i < s.length; ++i) {
            while (j > 0 && s[i] !== s[j]) j = next[j - 1];
            if (s[i] === s[j]) j++;
            next.push(j);
        }
        return next;
    }
    let next = getNext(s);
    if (next[next.length - 1] !== 0 && s.length % (s.length - next[next.length - 1]) === 0)
        return true;
    return false;
};

总结

这道题还有很多种解法,等有时间一定要尝试用不同的解法来解决问题。