题目:给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例 1:
输入: "abab"
输出: True
解释: 可由子字符串 "ab" 重复两次构成。
示例 2:
输入: "aba"
输出: False
示例 3:
输入: "abcabcabcabc"
输出: True
解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)
说是简单题目,但感觉应该接近中等题目的难度了。
说是一个字符串可由子字符串重复组成,那么字符串的长度必须是子串的整数倍,这样就可以少去很多的子串判断,并且字符串的每个子串部分的首字母head必然相同。
于是想到创建一个vector用来记录字符串每个首字母head的位置,而每个位置都可以确定一个子串,再由这些子串按滑动窗口的方式依次向后进行比对,程序中用times记录当前比较的次数以确定窗口的起始位置,窗口大小为子串长度,每次比对如果成功那么向后滑动一个当前子串的长度,如果失败则退出当前子串的比对,改为vector中下一个首字母head的位置重新创建一个子串,再重复上过程。如果最后比对到了字符串的尾部,则表示成功。注意字符串的长度必须子串的整数倍,否则就不用比对直接换到下一个子串。上代码:
class Solution {
public:
bool repeatedSubstringPattern(string s) {
int len = s.size();
char head = s[0];
vector<int> locs;
for (int i = 1; i != len; ++i) { //记录首字母在字符串中的位置
if (s[0] == head)
locs.push_back(i);
}
if (locs.empty()) return false; //如果除了s[0]不存在其他的首字母,则必然为false
int locsNum = 0; //确定当前子串到哪一个首字母位置之前,以确定窗口大小
string son = s.substr(0, locs[locsNum]); //确定子串
while (son.size() <= len / 2) { //主循环,子串长度必须小于字符串的一半
if (len % son.size() == 0) { //判断字符串长度是否是子串的整数倍
int times = 1; //记录当前子串情况下已经进行比对的次数,初始为1表示自身与自身比对已经完成
while (locs[locsNum] * times < len) { //子串比较的循环,一直比较到字符串结尾
if (son != s.substr(locs[locsNum] * times, son.size()))//子串与滑动窗口在在字符串中的位置进行比对,如果不同则表示当前子串不符合要求,退出比较
break;
++times; //当前比较相同,更新times以将窗口向后滑动
}
if (locs[locsNum] * times == len) //如果比对到了最后,表示符合要求,返回true
return true;
}
if ((++locsNum) == locs.size()) //表示所有首字母位置都已经用完也未找到适合的子串
return false;
son = s.substr(0, locs[locsNum]); //重新选取子串
}
return false; //子串长度已大于字符串的一半,未找到合适子串,返回false
}
};
执行用时: 60 ms, 在Repeated Substring Pattern的C++提交中击败了25.42% 的用户。
用时有点多,感觉kmp算法也可以,但是理解的不深就不用了。