题目描述
原题链接:459. 重复的子字符串
解题思路
一、移动匹配
总结来说,就是当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);
return t.find(s) != std::string::npos; // 当find找不到时,返回npos
}
};
下述代码是从头部后开始find
,如果找到一个s
,则说明含有匹配子串。
class Solution {
public:
bool repeatedSubstringPattern(string s) {
return (s + s).find(s, 1) != s.size();
}
};
二、暴力解法
class Solution {
public:
bool repeatedSubstringPattern(string s) {
int n = s.size();
for (int i = 1; i * 2 <= n; ++i) {
if (n % i == 0) {
bool match = true;
for (int j = i; j < n; ++j) {
if (s[j] != s[j - i]) {
match = false;
break;
}
}
if (match) {
return true;
}
}
}
return false;
}
};
三、KMP算法
KMP配合两个结论进行解题:
(1)字符串长度减去最长相等前后缀长度,得到的剩余部分,为可组成序列的最小重复子串,设长度为n’,n' = n - next[n - 1]
。
(2)当n可被n’整除时,说明可由该子串构成整个序列,当不能整除时,说明不能由该子串构成整个序列。
class Solution {
public:
void getNext(int n, string &s, int next[]) {
next[0] = 0;
for(int i = 1, j = 0; i < n; i++) {
while(j > 0 && s[i] != s[j]) j = next[j - 1];
if(s[i] == s[j]) j++;
next[i] = j;
}
}
bool repeatedSubstringPattern(string s) {
if(s.size() == 0) return false;
int n = s.size();
int next[n];
getNext(n, s, next);
// 当next最后一个不为0 而且 n - next[n - 1] 可以将n整除,则认为有子串
if(next[n - 1] != 0 && n % (n - next[n - 1]) == 0)
return true;
return false;
}
};
参考文章:459.重复的子字符串、重复的子字符串