对于一个字符串来说,定义一次循环移位操作为:将字符串的第一个字符移动到末尾形成新的字符串。
给定两个字符串 s1 和 s2,要求判定其中一个字符串是否是另一字符串通过若干次循环移位后的新字符串的子串。
例如 CDAA
是由 AABCD
两次移位后产生的新串 BCDAA
的子串,而 ABCD
与 ACBD
则不能通过多次移位来得到其中一个字符串是新串的子串。
输入格式
共一行,包含两个字符串,中间由单个空格隔开。
字符串只包含字母和数字,长度不超过 3030。
输出格式
如果一个字符串是另一字符串通过若干次循环移位产生的新串的子串,则输出 true
,否则输出 false
。
输入样例:
AABCD CDAA
输出样例:
true
/*什么是字串,例如"BCDAA"和"CDAA"这两个字符串,第二个字符串正好与第一个字符串的一个连续片段相同,
那么我们就说"CDAA"是"BCDAA"的字串,或者说"BCDAA"里面包含"CDAA"。
而这个题是判断循环移位后得到的字符串与短字符串的包含关系,那么首要任务就是把输入的长字符串先遍历,然后
再循环移位,再用这循环移位成功后得到的结果与字符串b比较,看b字符串是不是a变化后的字符串的子串。
要实现字符串的查找匹配,这是核心部分。具体见下面代码的分析。
*/
/*这个题与字符串匹配里面的BF算法有联系,后面最后字符串匹配就是用的这个,
如果忘了这个BF算法的原理与过程,可以再哔站收藏夹里面看讲解。*/
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
string a, b;
cin >> a >> b;
//首先是输入两个字符串
if (a.size() < b.size())
swap(a, b);
/*这里是对两个字符串进行判断,如果两个字符串一短一长,那么就把长的那个字符串给放到a字符串里面,到时后有利于
后面的操作;那个比较短的字符串就放在b字符串里面,以待后用。
*/
for (int i = 0; i < a.size(); i++)
{
a = a.substr(1) + a[0];
/*这第一个for循环,是为了遍历后循环移位最长的字符串a,得到其结果,为后一步的
字符串匹配做铺垫。
*/
for (int j = 0; j + b.size() <= a.size(); j++)
/*这里j+b.size()<=a.size;这句话的含义是:确保了子串a[j, j+b.size())的长度恰好等于字符串b的长度,
这样才有可能进行匹配。
这个j是从0开始的,j所在的值其实就是0和j之间的距离,而j+b.size()<=a.size这句话保证了我能够留足够的空间
来截取和b字符串长度一样的字符串,只有成功截取到这个字符串,才会有字符串匹配这个过程。
要是没有这句话的限制,j+b.size()<=a.size这句话就可能不成立了,有可能更大,这个时候b字符串反而比a字符串大,
那么两个字符串长度不同,那么肯定是匹配不成功的。所以这句话还是很有讲究的。
*/
{
//这个j的含义是截取的字符串的起点位置。
/*这第二个for循环的含义是:
从位置0开始,依次判断以位置j开始、长度为b.size()的子串是否与字符串b相同,
直到不足以构成长度为b.size()的子串为止。
这一句代码就是枚举,在得到循环移位后的结果字符串a'上,以a'字符串的起点0开始,我设一个变量j,
以b字符串长度为准,截取一段[j,j+b.size()]的字符串,通俗点就是以j为起点,往后截取一段和
b字符串长度相等的字符串。用截取的这个一小段连续的字符串与b字符串匹配和比较,如果截取的这
一小段字符串中的每个字符和b字符串的字符每个都一样,那么就说明是匹配成功了,那么就可以知道
此时b字符串就是a字符串的子串;j是一直在动态变化的,当j为一个起点,截取的字符串与b字符串匹配的
时候,有一个位置字符不同,那么这个时候的j起点就是不行的,在这个方位匹配就是错误的,这时候就是匹配
失败,失败后j继续往后移动,直到匹配成功为止。
说了这么多,就是把这个循环移位后的结果字符串a'当主串,b字符串当模式串,两者用来匹配,其中a'中
若是有一段连续的字符串和b字符串是一模一样的,那么就说明b字符串是a'字符串的的子串。
*/
int k = 0;
for (; k < b.size(); k++){
/*这第三个for循环执行的是b字符串与截取的字符串的字符串匹配。
这个k可以理解为匹配成功的字符的个数。这个k也是不断地动态变化的。
如果这两个字符串匹配成功的话,那么k就一直往后推进。注意哦,这个k是从b字符串的第一位,截取的
字符串起点j开始往后一位位的移动的。当b字符串中每个字符都匹配成功时,那么k就会到这个b字符串的
末尾停止,就是移动了b字符串长度那么远的距离,所以才会有当"k==b.size()"时,说明两个字符串匹配成功
了。反之,要是截取的字符串从起点j开始,往后的第j+k位,和b字符串中的第k位,有一位不相等,那么就说明
这两个字符串匹配失败,需要继续更新起点j再继续进行匹配,所以失败时就跳出这个k的循环,到外面
重新进入下一次循环,更新j值,进行下一轮操作。
*/
if (a[j + k] != b[k])
break;
}
//有一位没匹配上,就已经匹配失败了,需要更新。
/*注意:这一句判断语句只能加在k的for循环之外,因为k的for循环内层是用来匹配b里面的每个字符
和截取的字符串的每个字符是否相等。如果都相等的话,那么就是全部字符都匹配成功了,那么此时的
k值就与b字符串的长度一样了。这个时候再来判断k的值,如果相等,那么就说明b字符串是a'的子串。
这里要注意,那个k的for循环的那一对括号不要把下面这个if判断语句给括进去了,如果我们将if (k == b.size())放在内部循环中,那么每次只有在k等于b的大小时才会输出"true"。这样就会导致在内层循环的迭代过程中,只有当最后一个字符也匹配成功时才会输出"true"。这是不正确的,因为我们需要考虑的是整个b是否是a的循环变位,而不仅仅是最后一个字符。
所以,我们将if (k == b.size())放在内部循环的外层,在内层循环结束后判断k是否等于b的大小,这样可以确保我们检查了整个b与当前位置开始的a的子串的匹配情况。只有在所有字符都匹配成功时,我们才输出"true"。*/
if (k == b.size())
{
puts("true");
/*这里是匹配成功的情况,匹配成功后直接输入true,然后题就解决了,所以用return 0来结束这个
代码就OK。
*/
return 0;
}
}
}
puts("false");
//要是匹配失败,硬是枚举截取的每一段字符串都和b字符串不相同,说明b字符串那就不是a'字符串的子串,直接输出false。
return 0;
}
//更加细致的算法搜索BF算法和KMP算法即可。
解析和代码都在里面,还请大家耐心看完。