重复的子字符串
问题起源于一道leetcode题目:给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
该题目的简单解法如下:
class Solution {
public:
bool repeatedSubstringPattern(string s) {
return (s + s).find(s, 1) != s.length();
}
};
该题目实质就是判断一个字符串s是否为周期的,对于一个周期为T的函数,我们有f(x+T)=f(x),所以我们将两个s拼接为一个新字符串,并从这个新字符串的第二个元素即1号元素开始查找s,若s是周期字符串则find()方法返回值必小于新字符串中第二个s开始的位置,这是个充要条件。
字符串的最大公因子
对于字符串 S 和 T,只有在 S = T + … + T(T 与自身连接 1 次或多次)时,我们才认定 “T 能除尽 S”。返回字符串 X,要求满足 X 能除尽 str1 且 X 能除尽 str2。
class Solution {
public:
string gcdOfStrings(string str1, string str2) {
if (str1 + str2 != str2 + str1) {
return "";
}
return str1.substr(0, gcd(str1.size(), str2.size()));
}
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
};
这个题目是上一个题目的拓展,在判断字符串是周期的基础上还要找出最小周期,假设s既是str1的最小周期字串也是str2的最小周期字串,则存在正整数m,n使得str1=m×s,str2=n×s,易得str1+str2 == str2+str1是str1与str2有相同周期子串的充要条件。若str1与str2有相同周期子串则需要求出最小周期,即求两个整数的最大公因子问题,使用gcd(辗转相除法)算法即可。
字符串轮转
给定两个字符串s1和s2,编写代码检查s2是否为s1旋转而成(比如,waterbottle是erbottlewat旋转后的字符串)。
class Solution {
public:
bool isFlipedString(string s1, string s2) {
if (s1.size() != s2.size()) {
return false;
}
else if ((s1 + s1).find(s2) != std::string::npos) {
return true;
}
return false;
}
};
这道题目可以把s1看作周期为s1.size()的字符串,若s2由s1轮转而成,则s1+s1中必然找得到s2,这也是一个充要条件。