每日一题:459. 重复的子字符串

420 阅读1分钟

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

一、题目描述:

459. 重复的子字符串 - 力扣(LeetCode)

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

示例 1:

输入: s = "abab"
输出: true
解释: 可由子串 "ab" 重复两次构成。

示例 2:

输入: s = "aba"
输出: false

示例 3:

输入: s = "abcabcabcabc"
输出: true
解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)

提示:

  • 1 <= s.length <= 10^4
  • s 由小写英文字母组成

二、思路分析:

通过KMP算法计算出前后缀完全相等的长度max_size,由len(s)-max_size即可得出最短重复子串,再通过len(s)%max_size判断len(s)是否是max_size的倍数,以保证子串的完整性。
假设原字符串s为ababab,此时KMP计算得出前缀表为[0,0,1,2,3,4],此时最长前缀f为前abab,最长后缀l为后abab,s-l=ab(s[0:2]),又因为f[0]=l[0]、f[1]=l[1],带入原字符串下标可得s[0:2] = s[2:4],所以ab(s[0:2])即为最短重复子字符串,只需通过len(s)%len(ab)=>len(s) % (len(s)-len(l))判断字符串是否都是由最短重复子字符串构成即可。

三、AC 代码:

func KMP(s string)bool{
    // 变量初始化 
    i, n := 0, len(s)
    // 初始化前缀吧
    next := make([]int, len(s))
    for j := 1; j < len(s); j++{
        // 如果慢指针i不等于0且s[i]不等于s[j]
        for i > 0 && s[i] != s[j]{
            // 跳到上一个相等前后缀位置
            i = next[i-1]
        }
        // 如果快慢指针值相等,则慢指针移动一位代表相等前缀后缀长度+1
        if s[i] == s[j]{
            i += 1
        }
        // 更新next数组当前位置的相等前缀后缀长度
        next[j] = i
    }
    // 如果最长相等前缀后缀长度为0则说明不是由特定子字符串重复构建
    //如果非0则判断原字符串长度是否是最短重复子字符串长度的倍数
    //借此来判断原字符串是否全部由最短重复子字符串构成
    return next[n-1] != 0 && n % (n - next[n-1]) == 0
}

func repeatedSubstringPattern(s string) bool {
    // 返回结果
    return KMP(s)
}