最小循环子串

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;
}
不是所有的牛奶都叫特仑苏,也不是所有的人都叫猪猪
原文地址:https://www.cnblogs.com/chelsea0901/p/14313062.html