目录
问题描述:
实现代码与解析:
移动匹配法:
原理思路:
利用kmp法:
原理思路:
暴力法:
原理思路:
问题描述:
给定一个非空的字符串 s
,检查是否可以通过由它的一个子串重复多次构成。
示例 1:
输入: s = "abab" 输出: true 解释: 可由子串 "ab" 重复两次构成。
示例 2:
输入: s = "aba" 输出: false
示例 3:
输入: s = "abcabcabcabc" 输出: true 解释: 可由子串 "abc" 重复四次构成。 (或子串 "abcabc" 重复两次构成。)
实现代码与解析:
移动匹配法:
class Solution {
public:
bool repeatedSubstringPattern(string s)
{
string t=s+s;//拼接
t.erase(t.begin());//去头
t.erase(t.end()-1);//去尾
if(t.find(s)!=string::npos) return true;
return false;
}
};
还有一种简短的写法:
class Solution {
public:
bool repeatedSubstringPattern(string s) {
return (s + s).find(s, 1) != s.size();
}
};
原理思路:
满足条件的字符串一定可以找到前后子串相等的子串,我们将子串复制一份拼接在一起,这时,中间的字符串变为后子串加前子串,在其中找到原子串,说明这个字符串可由一个子串重复构成,至于为什么删除首字符和尾字符,因为我们要排除找到的是自身的字符串,确保找到的是我们拼接而来的。
第二个简短代码,它是从下标为1开始找的,若不等于s.size(),也就是在找到末尾一定匹配的子串前,在中间没找到匹配的,所以说明该字符串不能由子串重复构成,与删除首尾字符原理相同。下面给出一个例子,便于理解。
利用kmp法:
class Solution {
public:
//kmp获得next数组
void getNext(int next[],string s)
{
int j=0;
next[0]=0;
for(int i=1;i<s.size();i++)
{
while(s[j]!=s[i]&&j>0)
{
j=next[j-1];
}
if(s[j]==s[i])
{
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]!=0&&len%(len-next[len-1])==0)
{
return true;
}
return false;
}
};
原理思路:
先说结论:前缀中,不包含最长公共前后缀相同的部分,即为组成该字符串的重复子串。原理需要推到,大家只要记住,前缀和后缀对应的位置字符相同,字符串数组中同一个下标的字符相同,这两点,就能理解为什么得出的这个结论。同样给出图片,便于大家理解:
根据红线的连接就可以看出,最小重复子串是如何找出来的了,看不懂的话再看看我让大家注意的那两点。
暴力法:
class Solution {
public:
bool repeatedSubstringPattern(string s)
{
int len=s.size();
for(int i=1;i<=len/2;i++)
{
string temp;
if(len%i!=0)
{
continue;
}
//完整拼接复原
for(int j=0;j<len/i;j++)
{
//拼接子串
for(int k=0;k<i;k++)
{
temp+=s[k];
}
}
if(temp==s)
{
return true;
}
}
return false;
}
};
原理思路:
直接暴力法把所有的可能找出来,比较简单和好理解,就是循环遍历,根据倍速拼接,然后复原,看是否与原字符串相等。