zoukankan      html  css  js  c++  java
  • 最小循环子串

    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;
    }
    
    不是所有的牛奶都叫特仑苏,也不是所有的人都叫猪猪
  • 相关阅读:
    浅谈HTTP中Get与Post的区别
    js 执行完setTimeout再接着执行函数
    2017年书单
    js判断img是否存在
    md5
    GIF播放器
    java 集合(二)
    java 泛型
    抓包工具
    js计算地球两个经纬度之间的距离
  • 原文地址:https://www.cnblogs.com/chelsea0901/p/14313062.html
Copyright © 2011-2022 走看看