牛客网暑期ACM多校训练营(第三场)- E - Sort String (KMP next数组寻找字符串循环节)(和另一种方法)

题目链接:

链接:https://www.nowcoder.com/acm/contest/141/E
来源:牛客网

题目:

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
Special Judge, 64bit IO Format: %lld

题目描述

Eddy likes to play with string which is a sequence of characters. One day, Eddy has played with a string S for a long time and wonders how could make it more enjoyable. Eddy comes up with following procedure:

1. For each i in [0,|S|-1], let Si be the substring of S starting from i-th character to the end followed by the substring of first i characters of S. Index of string starts from 0.
2. Group up all the Si. Si and Sj will be the same group if and only if Si=Sj.
3. For each group, let Lj be the list of index i in non-decreasing order of Si in this group.
4. Sort all the Lj by lexicographical order.

Eddy can’t find any efficient way to compute the final result. As one of his best friend, you come to help him compute the answer!

输入描述:

Input contains only one line consisting of a string S.

1≤ |S|≤ 106
S only contains lowercase English letters(i.e. ).

输出描述:

First, output one line containing an integer K indicating the number of lists.
For each following K lines, output each list in lexicographical order.
For each list, output its length followed by the indexes in it separated by a single space.

 

示例1

输入

复制

abab

输出

复制

2
2 0 2
2 1 3

示例2

输入

复制

deadbeef

输出

复制

8
1 0
1 1
1 2
1 3
1 4
1 5
1 6
1 7

题目大意:

给你一个字符串,然后在第 i 个字符后面,把字符串分成两段,然后字符串的这两部分前后互换,得到一个新的字符串 Si 。比如 串 abcdef ,S1 就是 a bcdef ,然后交换前后得到 bcdef a。然后给你一个字符串,让你把所有的 Si 分类,完全相同的为一类。然后输出分类信息(按照 i 的字典序排序)。

分析:

通过观察,我们发现,Si 是随着 S 的循环节重复的,比如 abcdabcd这个串 ,我们这样得到的Si  abc | dabcd 和这样得到的 Si abcdabc | d 是一样的。那么,这个题的关键就变成了寻找字符串的循环节。字典序输出的话,我们顺序输出就能保证字典序是最小的。

那么应该怎么去找字符串的循环节呢,正解应该是KMP算法中NEXT数组的应用,但我写这个题的时候,并不会KMP算法,所以我就自己想了一个方法来找循环节,具体的时间复杂度我也不会分析(蒟蒻啊)。

《牛客网暑期ACM多校训练营(第三场)- E - Sort String (KMP next数组寻找字符串循环节)(和另一种方法)》

从运行时间来看,复杂度应该还可以。(也可能是数据的问题)(话说有人Hash写的 900ms)

我的想法是,既然一个字符串具有循环节,那么,它一定可以分成循环节出现次数的份数,比如 abababab 就可以分成 ab ab ab ab,那么,我们只要去尝试所有的分发,并判断各部分是否相等就好了。(也可以用二分来枚举,我也不知道哪一种更快)那么,如果一个字符串能分成4份,就一定可以先分成两份,其他的数也是这样,能分成6份,一定能分成三份,也就是说,我们可以,先用素数去划分这个字符串(因为上面的过程类似素数筛法),然后在去划分这个串切割成的子串。可以用递归去完成这一过程。

代码:

#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<vector>
  
using namespace std;
  
const int MAXN=1e6+10;
int num,len;
bool prime[MAXN];
vector<int> P;
  
void Init_Prime(int N)
{
    prime[1]=true;
    for(int i=2;i<=N;i++)
    {
        if(!prime[i])
        {
            P.push_back(i);
            for(int j=i+i;j<=N;j+=i)
                prime[j]=true;
        }
    }
}
  
int solve(string &t)
{
    int tlen=t.length();
    int ans=1;
    for(int i=0;i<num&&P[i]<=tlen;i++)
    {
        if(tlen%P[i]==0)
        {
            int tt=tlen/P[i];
            int flag=0;
            string temp=t.substr(0,tt);
            for(int j=tt;j<tlen;j+=tt)
            {
                if(t.substr(j,tt)==temp)
                    temp=t.substr(j,tt);
                else
                {
                    flag=1;
                    break;
                }
            }
            if(flag==0)
                ans=P[i]*solve(temp);
        }
    }
    return ans;
}
  
int main()
{
    string s;
    cin>>s;
    Init_Prime(len=s.length());
    num=P.size();
    int ans=solve(s);
    int tlen=len/ans;   
    printf("%d\n",tlen);
    for(int i=0;i<tlen;i++)
    {
        printf("%d %d",ans,i);
        for(int j=1;j<ans;j++)
            printf(" %d",j*tlen+i);
        putchar('\n');
    }
    return 0;
}

然后有去学习了KMP算法,发现最小循环节的长度可以直接用 len-next[len] 来表示,学到了!学到了!(len代表输入串的长度,next是该字符串的失配函数)

《牛客网暑期ACM多校训练营(第三场)- E - Sort String (KMP next数组寻找字符串循环节)(和另一种方法)》

KMP通过的代码如下(完全就是模板题了有木有!!):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
  
using namespace std;
  
const int MAXN=1e6+10;
char s[MAXN];
int nextnum[MAXN];

void Get_fail(char* P, int *f)
{
	int m=strlen(P);
	f[0]=0;f[1]=0;
	for(int i=1;i<m;i++)
	{
		int j=f[i];
		while(j&&P[i]!=P[j]) j=f[j];
		f[i+1]= P[i]==P[j]?j+1:0;
	}
}
  
int main()
{
    scanf("%s",s);
    int len=strlen(s);
    Get_fail(s,nextnum);
    int ans=len-nextnum[len];
    int tlen=len/ans;    
    printf("%d\n",tlen);
    for(int i=0;i<tlen;i++)
    {
        printf("%d %d",ans,i);
        for(int j=1;j<ans;j++)
            printf(" %d",j*tlen+i);
        putchar('\n');
    }
    return 0;
}

 

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/weixin_42165981/article/details/81265463
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞