CodeForces - 1473B
题意:
n对字符串,对于每对字符串,如果没有“最小公倍串”,就输出-1,如果有就输出其最小公倍串,如:abab 和 ab有最小公倍串abab,而aba和ab则没有。
思路:
这个题是对KMP的next数组的灵活运用
定理:
假设S的长度为len,若S存在最小循环子串,则循环长度c_len = len - next[len],子串为S[0…len - next[len] - 1]
(1)如果len % (len - next[len]) == 0,则表明S串完全由循环节循环构成,循环次数 = len / (len - next[len])
(2) 如果len % (len - next[len])!= 0 说明S串需要添加几个字母才能成为完全由循环节构成的串,这些需要加的字符的数量是c_len - len % c_len 而需要添加的字符是S串的第len % c_len + 1个字符开始,到c_len的字符结束
next数组
next[i] 表示前面长度为i的子串中,前缀和后缀相等的最大长度
定理的理论解释
-
对于一个字符串,如abcdabcdabcd,由长度为4的子串abcd循环三次得到,next[len] = 8,即原串的前八位等于后八位
-
也就是说,对于某个字符串S,长度为len,由长度为L的字符串s重复R次得到,当R>= 2时,必然有S[0..len-L-1]=S[L..len-1]
-
对于KMP算法来说,就有next[len] = len - L,此时的L必然是最小的,(因为next数组是前缀和后缀相等的最大长度,即len - L最大,而len是确定的,所以L最小)
将这个式子移项,即可得到开头的定理c_len = len - next[len]
具体证明在这:传送门
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <sstream>
#include <map>
#include <set>
using namespace std;
int next1[100005],next2[100005], len1, len2;
string s1, s2;
void getnext1()//第一个字符串的next数组的构造
{
int j, k;
j = 0;
k = -1;
next1[0] = -1;
while(j < len1)
{
if(k == -1 || s1[j] == s1[k])
next1[++j] = ++k;
else
k = next1[k];
}
}
void getnext2()//第二个字符串的next数组的构造
{
int j, k;
j = 0;
k = -1;
next2[0] = -1;
while(j < len2)
{
if(k == -1 || s2[j] == s2[k])
next2[++j] = ++k;
else
k = next2[k];
}
}
int gcd(int a, int b)//求最大公约数,以便求最小公倍数
{
if(b) return gcd(b, a % b);
else return a;
}
int main()
{
int n, a, b, k;
cin>>n;
while(n--)
{
memset(next1, 0, sizeof(next1));//数组清0
memset(next2, 0, sizeof(next2));
k = 1;
cin>>s1>>s2;
len1 = s1.size();
len2 = s2.size();
getnext1();//别忘了写函数,不然你都不知道为什么不对
getnext2();
a = len1 - next1[len1];//代表循环串的长度
b = len2 - next2[len2];
if(len1 % a != 0)//判断是不是完全由循环串组成,如果不是,则把整个串当做循环串(我当时就没注意,wa了
a = len1;
if(len2 % b != 0)
b = len2;
if(a != b)
{//两个最小的循环串的长度不相等,则必然不能符合题意
cout<<-1;
}
else
{
for(int i = 0; i < a; i++)//判断循环串是否相同
{
if(s1[i] != s2[i])
{
k = 0;
break;
}
}
if(k == 0)
{
cout<<-1;
}
else//相同才能继续输出
{
int c = (len1 / a * len2 / b) / gcd(len1 / a, len2 / b);
while(c--)
{
for(int i = 0; i < a; i++)
cout<<s1[i];
}
}
}
cout<<endl;
}
return 0;
}