题目:
- 给定一个非空的字符串
s
,检查是否可以通过由它的一个子串重复多次构成。
示例:
- 输入:s = “abab”
- 输出:true
- 解释:可由子串 “ab” 重复两次构成。
版本1:朴素暴力版
class Solution {
public:
bool repeatedSubstringPattern(string s) {
int size = s.size(); // 输入串的长度
string temp = ""; // 临时字符串,保存需要比较的串
for(int i = 0; i < size / 2; i++){ // 只需要遍历一遍,也就是第一个字符开始,到中间位置,就能判断出结果了
temp += s[i]; // 加入当前字符
if(size % temp.size() == 0){ // 如果 s 的长度是 temp 的整数倍,才有进一步判断的必要,否则一定不会匹配
int k = (size / temp.size()); // 添加 k 次 temp 字符串
string res;
while(k--){
res.append(temp);
}
if(res == s) return true; // 作比较,若相等则返回true
res.erase();
}
}
return false;
}
};
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
版本2:移动匹配版
- 思路:当将两个
s
拼接在一起时,若存在可以用子串构成的s
,那么一定可以找到。但是要掐头去尾,不然find
的时候会找到第一个s
。注意:find
找不到的时候会返回string::npos
。
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;
}
};
时间复杂度:
O
(
n
)
O(n)
O(n)
空间复杂度:
O
(
1
)
O(1)
O(1)
版本3:KMP版
KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
class Solution {
public:
void getNext(int* next, const string& s){
next[0] = -1;
int j = -1;
for(int i = 1; i < s.size(); i++){
while(j >= 0 && s[i] != s[j + 1]) j = next[j];
if(s[i] == s[j + 1]) j++;
next[i] = j;
}
}
bool repeatedSubstringPattern(string s) {
if(s.size() == 0) return false;
int next[s.size()];
getNext(next, s);
int len = s.size();
if(next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0) return true;
return false;
}
};