例题
Leetcode.459. 重复的子字符串
给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = “abab”
输出: true
解释: 可由子串 “ab” 重复两次构成。
示例 2:
输入: s = “aba”
输出: false
示例 3:
输入: s = “abcabcabcabc”
输出: true
解释: 可由子串 “abc” 重复四次构成。 (或子串 “abcabc” 重复两次构成。)
一、字符串匹配法
本题如果采用枚举法,时间复杂度太高,暴力解法不建议在本题上使用,我们可以采用字符串匹配的方法解决这个问题。
已知一个字符串s ="ababab"是由"ab"组合得来的,那么我们就可创建一个字符串str = s + s = “abababababab”,str字符串是两个s连接而成,此时我们可以使用find()函数跳过s[0],从s[1]开始搜索与字符串s相匹配的子字符串,我们知道find()函数返回值是子字符串 s 在文本串(被搜索的字符串也就是str)设置位置开始第一次与之匹配的位置,由于str = s + s,那么find()的返回值如果是第二个字符串的开头,那就说明字符串s中没有重复的子字符串,否则就会在第二次重复的位置找到。
class Solution {
public:
bool repeatedSubstringPattern(string s) {
return (s + s).find(s, 1) != s.size();
}
};
二、KMP方法
那么难度再提高一点,复杂度不变的情况下,如何不增加额外的内存空间,完成这道题目呢?我们可以采用之前学习的KMP算法的知识点。
在KMP算法中,我们计算过前缀表,我们知道前缀表中存储着最长公共前缀表,若记最长公共前后缀的长度为l,字符串长度为len,当l = 0时字符串肯定没有重复子串不用考虑,当l > 0 时,len - l的长度就等于重复最短字串的长度或者重复最短字串的长度加上不重复的字串,我们知道两个数相乘之和等于相加之和的正整数只有2、2,所以如果len-l是重复最短字串的长度加上不重复的字串那么就一定不能被len整除。
公共前后缀的概念一定要搞清楚。
class Solution {
public:
int getnext(string s)
{
int next[s.size()+2];//为利方便计算next数组从next[1]开始计算,所以大小要比s.size()大1,因为要计算加上字符串最后一位的前缀表值所以再加1
//KMP算法匹配时是不加上最后一个字符计算的,本题计算的不同所以加上了
int i = 1,j = 0;
next[1] = 0;
while(i < s.size() + 1)
{
if(j == 0 || s[i-1] == s[j-1])
{next[++i] = ++j;}
else
{j = next[j];}
}
return next[s.size()+1];
}
bool repeatedSubstringPattern(string s) {
int len = s.size();
if(len > 1)
{
int l = getnext(s)-1;//因为为了方便计算公共前后缀,其值都加了1,真正值要-1
if(l>0 && len%(len-l)==0)
{return true;}
}
return false;
}
};