Description
Given a non-empty string check if it can be constructed by taking a substring of it and appending
multiple copies of the substring together. You may assume the given string consists of lowercase
English letters only and its length will not exceed 10000.
Example 1
Input: "abab"
Output: True
Explanation: It's the substring "ab" twice.
Example 2
Input: "aba"
Output: False
Example 3
Input: "abcabcabcabc"
Output: True
Explanation: It's the substring "abc" four times. (And the substring "abcabc" twice.)
Solution 1(C++)
class Solution {
public:
bool repeatedSubstringPattern(string s) {
int len=s.size(), i=1, j, k;
while(i<len){
if(len%i==0){
for(j=0; j<i; j++){
for(k=0; k<len/i; k++){
if(s[j+i]!=s[j+k*i]) break;
}
if(k!=len/i) break;
}
if(j==i) return true;
}
i++;
}
return false;
}
};
Solution 2(C++)
class Solution {
public:
bool repeatedSubstringPattern(string str) {
int i = 1, j = 0, n = str.size();
vector<int> dp(n+1,0);
while( i < str.size() ){
if( str[i] == str[j] ) dp[++i]=++j;
else if( j == 0 ) i++;
else j = dp[j];
}
return dp[n]&&dp[n]%(n-dp[n])==0;
}
};
Solution 3(C++)
class Solution {
public:
bool repeatedSubstringPattern(string s) {
return (s+s).find(s,1) < s.size();
}
};
算法分析
解法一泛性不够,也就适合这一道题目。解法三比较取巧。我就不多说了。
重点要说的是解法二,是基于KMP算法改进的。关于KMP算法我觉得另外写一篇博客来系统学习一下。
//===================
说回这个解法二,dp[i+1]其实是储存到位置i为止的字符串重复的最大字符数。举个例子:“abcabcabc”,那么按照解法二的算法来计算,就dp就应该是:0001234560。那么这样结果return时,就是6%(9-6)=0。所以返回true了。
那么这个解法,观察到如果一个字符串是符合题目要求的,那么,字符串中必然有重复出现的字符。这个重复出现还要是保持等间距的重复出现。比如之前的例子,最开始出现的‘a’,后面一定会再出现’a’,而且要隔两个字符。同样的道理‘b’、‘c’都可以。
所以,我们整理思路,设定两个“指针”,当发现相同的字符之后,两个指针保持一步长不断增长,如果这样顺利遍历完整个字符串,那么说明字符串是可重复的。但如果中间发现两个指针的字符不相同,那么就说明有问题。那是不是就说这样的字符串就是不对的呢?也不是,举个例子:“abacabac”。这样一个字符串,按照之前的算法设计的话,最开始a重复,然后下一步两个指针一个是‘b’,一个是‘c’。不相同,那就能直接输出false吗,不能。
为了考虑这种情况,我们就应该在不匹配的时候重新开始匹配。具体到例子来说,就是上面我们选的两个a的距离太短,要换。那该怎么换呢?
其实慢慢的,就会发现,这也是一个字符串匹配问题。就涉及到KMP算法了。只不过,KMP算法最原始问题是解决两个给定字符串的匹配问题。但这里,变成了一个字符串能不能和他的子字符串匹配的问题了。而且子字符串的选择也是多种多样的。
但是,我们可以借用KMP算法中的一些思想,来帮助我们解决刚才的问题。该如何重新开始匹配。回顾一下KMP算法中遇到匹配失败时的做法,也许能给我们一些启示。
KMP算法中,当匹配失败时,采用的方法不是和暴力算法一样,从头开始,重新从目标字符串的下一个字符开始匹配模板字符串的第一个字符。而是利用事先计算好的next数组,找到已匹配的模板字符串中,所有前缀字符串与后缀字符串的最大公共子字符串的长度,说明目标字符串与模板字符串已匹配部分还有”这个长度大小”的子字符串是完全相同的。所以,我们不需要重新开始匹配,而是将模板字符穿移动几个字符,使得“这个长度大小”的子字符串,正好匹配目标字符串与模板字符穿。
那么类似的,我们迁移过来。…..
=======================//
上面是2018年3月7号第一次想解释这个算法的。但是后面卡住了,我还没想清楚如何解释j=dp[j]。这一句,目前疑惑点,就是dp本来说是重复的字符数,那为啥字符数能当成位置索引传给j,在匹配值失败之后。我尝试将这一句改为j=0。然后对于字符串:”abaababaab”。也许需要好好学习一下KMP算法。
程序分析
略。