牛客网暑期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表示的是,在这个串中被重复使用到的字符), t<kt t < k − t 的时候,就是我们所说的第二种情况

对于第一种情况,当 t>kt t > k − 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值