玲珑杯 第4次 String cut(暴力字符串)

题意:给你长度为n(<=100000)的字符串,问你任意删除一个字符后得到循环节最多的数量是多少

 

题解:最简单的想法就是枚举删除的字符,再kmp求循环节,但是时间复杂度为O(n*n)会超时

因此我们需要这样,需要删除的字符我们不能枚举,只能通过一些特别的方法来寻找或者判断

我们根据kmp求循环节的想法来枚举循环节长度(相当于是答案),因为循环节长度是(n-1)的因子少于log2(n)个

所以我们枚举了循环节的长度后可以使用O(n)来判断长度为m的循环节是否满足条件

此时我们可以这样想:先假设我们需要删除的字符在第一个循环节内,此时我们先找出[0,m]的字符,然后[m+1,2*m]与[0,n]

进行枚举匹配,当不同时就将[0,n]这一个向后跳一个在比较(当然只能跳一次),否则当需要删除的字符在第一个循环节后面,

这样我们[0,m-1]就一定是循环节,这样再与[0,n]枚举匹配判断就好了

 

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<iomanip>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define eps 1E-8
/*注意可能会有输出-0.000*/
#define Sgn(x) (x<-eps? -1 :x<eps? 0:1)//x为两个浮点数差的比较,注意返回整型
#define Cvs(x) (x > 0.0 ? x+eps : x-eps)//浮点数转化
#define zero(x) (((x)>0?(x):-(x))<eps)//判断是否等于0
#define mul(a,b) (a<<b)
#define dir(a,b) (a>>b)
typedef long long ll;
typedef unsigned long long ull;
const int Inf=1<<28;
const ll INF=1ll<<60;
const double Pi=acos(-1.0);
const int Mod=1e9+7;
const int Max=1e5-7;
char str[Max];
string tem;
int Jud(int n,int dig,int num)//n长度 dig循环节长度 num循环节个数
{
    tem.clear();
    for(int i=0; i<dig; ++i)//假设[0,m-1]为循环节
        tem+=str[i];
    int flag=0;
    for(int i=dig; i<n; ++i)
    {
        if(tem[(i+flag)%dig]!=str[i])//
        {
            if(flag==-1)
            {
                flag=-2;
                break;
            }
            else
            {
                flag=-1;
            }
        }
    }
    if(flag!=-2)
        return 1;
    tem.clear();
    for(int i=dig+1; i<dig+dig+1; ++i)//[m+1,2*m]为循环节
        tem+=str[i];

    flag=0;
    for(int i=0; i<n; ++i)
    {
        if(tem[(i+flag)%dig]!=str[i])
        {
            if(flag==-1)
            {
                flag=-2;
                break;
            }
            else
            {
                flag=-1;
            }
        }
    }
    if(flag!=-2)
        return 1;
        return 0;
}
int main()
{
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        scanf("%s",str);
        for(int i=1; i<=n-1; ++i)//枚举循环节长度
        {
            if(i==n-1||(n-1)%i==0&&Jud(n,i,(n-1)/i))//判断
            {
                printf("%d\n",(n-1)/i);
                break;
            }
        }
    }
    return 0;
}

 

posted @ 2016-11-06 01:04  专注如一  阅读(395)  评论(0编辑  收藏  举报