算法题
字符串(string)
重复的子字符串

重复的子字符串

题目

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

示例 1:

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

示例 2:

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

示例 3:

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

思路

暴力的解法, 就是一个 for 循环获取子串的终止位置, 然后判断子串是否能重复构成字符串,又嵌套一个 for 循环,所以是 O(n^2) 的时间复杂度。

以第一个字母为开始的子串就可以,所以一个for循环获取子串的终止位置就行了。 而且遍历的时候 都不用遍历结束,只需要遍历到中间位置,因为子串结束位置大于中间位置的话,一定不能重复组成字符串。

另外一个解法就是 KMP 。

解法

暴力解法:

func repeatedSubstringPattern(s string) bool {
    n := len(s)
    for i := 1; i <= n / 2; i++ {
        if n % i == 0 {
            if check(s, i) {
                return true
            }
        }
    }
    return false
}
 
func check(s string, l int) bool {
    n := len(s)
    for i := l; i < n; i++ {
        if s[i] != s[i - l] {
            return false
        }
    }
    return true
}

KMP 解法:

func repeatedSubstringPattern(s string) bool {
    return kmp(s + s, s)
}
 
func kmp(s, p string) bool {
    n, m := len(s), len(p)
    next := make([]int, m)
    for i, j := 1, 0; i < m; i++ {
        for j > 0 && p[i] != p[j] {
            j = next[j - 1]
        }
        if p[i] == p[j] {
            j++
        }
        next[i] = j
    }
    for i, j := 0, 0; i < n; i++ {
        for j > 0 && s[i] != p[j] {
            j = next[j - 1]
        }
        if s[i] == p[j] {
            j++
        }
        if j == m {
            return true
        }
    }
    return false
}

移动匹配:

func repeatedSubstringPattern(s string) bool {
    return strings.Contains((s + s)[1:len(s) * 2 - 1], s)
}