牛客网暑期ACM多校训练营(第三场) E Sort String 【kmp】
题目大意: 给一个字符串,然后将字符串前i个字符移到组字符串的后面,组成新的字符串,如果有遇到相同的字符串分为一组,然后问有多少组,每组按字典序输出字符串的下标
例如 abab
i = 0 的时候,就是前0个字符串移到后面 也就是 abab
i =1 的时候 就是 baba
i=2 的时候 ,就是 abab
i=3的时候,就是 baba
所以有两组相同的,每组有两个数
题目分析,这个题我们就是发现对于整个字符串来说,去找能够覆盖整个字符串的重复的串即可,这样我们想到了KMP中的NEXT的数组的定义,next 数组 就是记录当前字符串中,相同的前缀和后缀的最大长度,因此,只要这个最大长度 k *2 >=len 就可以找到这样重复的字符串
那么当 k*2>=len的情况下
有以下几种情况
1、恰好覆盖不重复
2、覆盖并有重复
我们先分析第二种情况,比较简单,举个例子 cabcabc 这里面重复的字符串cabc,那么整个字符串中只能找到两个这样的字符串。令 t= k*2-len (t表示的是,在这个串中被重复使用到的字符), 的时候,就是我们所说的第二种情况
对于第一种情况,当 的时候,也就是说重复的串长度比剩余的串长度长,那么我们很容易发现,重复的串一定是剩余的串m次重复,这样的话,整个字符串就由剩余的串 重复m+2次组成
对于恰好覆盖的情况,子串中的每一个字符都可能是串的起始位置,可以组成 k-t个不同的group
最后处理剩余的不组成group的字符,就可以解决了;
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+50;
const int inf=0x3f3f3f3f;
string s;
int Next[maxn];
void getNext()
{
int i=0;
int j=-1;
Next[0]=-1;
int len=s.size();
while(i<len)
{
if(j==-1||s[i]==s[j])Next[++i]=++j;
else j=Next[j];
}
}
vector<int>ans[maxn];
int vis[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>s;
getNext();
int n=s.size();
int k=Next[n];
int idx=0;
memset(vis,0,sizeof(vis));
if(k*2>=n)
{
int t=k*2-n;
int p=k-t;
if(t<p&&t>0)
{
ans[0].push_back(2);
ans[0].push_back(0);
ans[0].push_back(n-k);
vis[0]=1;
vis[n-k]=1;
idx++;
}
else
{
int cnt = t/p;
for(int i=0;i<p;i++)
{
ans[i].push_back(cnt+2);
for(int j=0;j<cnt+2;j++)
{
ans[i].push_back(i+(j*p));
vis[i+(j*p)]=1;
}
}
idx=p;
}
}
for(int i=0;i<n;i++)
{
if(!vis[i])
{
ans[idx].push_back(1);
ans[idx++].push_back(i);
}
}
cout<<idx<<"\n";
for(int i=0;i<idx;i++)
{
for(int j=0;j<=ans[i][0];j++)
{
if(j!=ans[i][0])
cout<<ans[i][j]<<" ";
else cout<<ans[i][j]<<"\n";
}
}
return 0;
}