代码随想录二刷 | 字符串 |重复的子字符串

代码随想录二刷 | 字符串 |重复的子字符串

  • 题目描述
  • 解题思路 & 代码实现
    • 移动匹配
    • KMP算法

题目描述

459.重复的子字符串

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

示例 1:

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

示例 2:

输入: s = “aba”
输出: false

示例 3:

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

提示:

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

解题思路 & 代码实现

移动匹配

如果一个字符串是由重复的子串构成,也就是由前后相同的子串构成。那么用后面的子串做前串,前面的子串做后串,也依然能组成原来的字符串。

所以判断字符串 s 是否由重复子串组成,只要两个s拼在一起里面还能出现一个s,就说明s是由重复子串组成的。

在判断 s + s 拼接的字符串时,要刨除 s + s 的首字符和尾字符,这样避免在 s + s中搜索出原来的s,毕竟我们要搜索的是中间拼接出来的 s。

class Solution {
public:
	bool repeatedSubstringPattern(string s) {
		string t = s + s;
		t.erase(t.begin());
		t.erase(t.end() - 1);
		if (t.find(s) != std::string::npos)
			return true;
		return false;
	}
};

KMP算法

在重复子串组成的字符串中,最长相等前后缀不包含的子串就是最小重复子串,拿字符串s = "abababab"举例:
代码随想录二刷 | 字符串 |重复的子字符串_第1张图片
假设字符串 s 由 n 个重复子串构成,重复子串的长度为x,所以 s = n * x

因为字符串 s 的最长公共前后缀的长度一定不是 s 本身,所以最长相同前后缀的长度为m * x,而且n - m = 1。这里可以从上图中看出,n 和 m 正好相差一个 x 的长度。

所以,如果nx % (n - m) * x = 0,说明s的长度可以整除一个重复子串的长度,也就说明有重复出现的子字符串。

假设数组的长度为len,那么最长公共前后缀的长度就是next[len - 1] + 1,这里+1是因为采用了统一减一的操作。

那么,数组的长度 - 最长公共前后缀就是一个x的长度,只要数组的长度能整除 x 的长度,那么就存在重复的子字符串。

那么,套用上面的nx % (n - m) * x,可得len % (len - (next[len - 1] + 1)) == 0

class Solution {
public:
	void getNext(int* next, const string& s) {
		int j = -1;
		for (int i = 1; i < next.size(); i++) {
			while (j >= 0 && next[i] != next[j + 1]) {
				j = next[j];
			}
			if (next[i] == next[j + 1]) {
				j++;
			}
			next[i] = j;
		}
	}

	bool repeatedSubstringpattern(string s) {
		if (s.size() = 0) {
			return false;
		}
		int next[s.size()];
		getNext(next, s);
		len = s.size();
		if (next[len - 1] + 1 != 0 && len % (len - (next[len - 1] + 1)) == 0) {
			return true;
		}
		return false;
	}
};

你可能感兴趣的:(代码随想录二刷,算法,c++)