给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:
输入: “abab”
输出: True
解释: 可由子字符串 “ab” 重复两次构成。
示例 2:
输入: “aba”
输出: False
示例 3:
输入: “abcabcabcabc”
输出: True
解释: 可由子字符串 “abc” 重复四次构成。 (或者子字符串 “abcabc” 重复两次构成。)
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/repeated-substring-pattern
代码思路:
这道题的思路如果熟悉kmp的算法,这道题还是比较简单的。
kmp的算法在于找出字符串中出现的前缀。
例如:
1 2 3 4 5 6 7 8 9 10 11
a b c a a a a b c d d
-1 0 0 0 0 0 0 1 2 0 0
上面的例子中在789号位置上匹配前缀abc成功,所以就可以知道这个字符串中有自己的前缀abc出现在字符串中。
回到这道题,如果是一个由一个前缀重复构成的字符串,那么由kmp算法分析后的数组特点如下:
- 开始的前缀一定是-1 0 0 0
- 后续一定是逐步增一的数组
例如:
0 1 2 3 4 5 6 7 8
a b c a b c a b c
-1 0 0 0 1 2 3 4 5
为了匹配整个字符串所以要对这个字符串进行以下处理。
0 1 2 3 4 5 6 7 8 9
a b c a b c a b c #
-1 0 0 0 1 2 3 4 5 6
然后就可以知道匹配到最后的前缀是什么,
9-6=3,所以前三个字符构成的字符串就是被重复的子字符串。
然后再次进行检索,整个字符串是否确实由这个前缀重复组成的,返回对应结果即可。
public boolean repeatedSubstringPattern1(String s) {
int curr=0;
int count=-1;
s=s+"#";
int[] temp=new int[s.length()];
temp[0]=-1;
//kmp算法
for(int i=1;i<s.length();){
if(count==-1||s.charAt(i-1)==s.charAt(count)){
temp[i]=++count;
i++;
}else{
count=temp[count];
}
}
curr=0;
s=s.substring(0,s.length()-1);
//如果最后是一个0,说明字符串最后一个字符不对,所以返回错误
if(temp[s.length()]==0)return false;
//然后对整个字符串进行比对
for(int i=s.length()-temp[s.length()];i<s.length();i++){
if(curr==s.length()-temp[s.length()]){
curr=0;
}
if(s.charAt(i)==s.charAt(curr)){
curr++;
}else{
return false;
}
}
//比对完成后,根据比对返回结果
if(curr==s.length()-temp[s.length()]){
return true;
}else{
return false;
}
}
结果如下:
代码分析:
时间复杂度:O(2n),搜索了两遍。
进一步分析:
如果是符合要求的字符串找到的子串的长度一定是字符串总长度的约数。
a b c e e e a b c #
-1 0 0 0 0 0 0 1 2 3
前缀的计算9-3=6,所以重复的前缀就是abceee,因为后续只有abc所以说明如果有后续可能的被重复的前缀是abceee,但是现在总的字符长度只有9,这个子串长度有6所以这个字符串不符合要求。
改进代码如下:
public boolean repeatedSubstringPattern2(String s) {
int curr=0;
int count=-1;
s=s+"#";
int[] temp=new int[s.length()];
temp[0]=-1;
for(int i=1;i<s.length();){
if(count==-1||s.charAt(i-1)==s.charAt(count)){
temp[i]=++count;
i++;
}else{
count=temp[count];
}
}
s=s.substring(0,s.length()-1);
return temp[s.length()] != 0 && s.length() % (s.length() - temp[s.length()] ) == 0;
}
结果如下:
代码分析:
时间复杂度:O(n)。只有kmp算法对字符串进行了一次扫描。