计蒜客 旋转数字 (kmp+exkmp)

 首先得去重,怎么去重呢,刚开始想了好几种方法,都不太理想,后再才想明白,找重复的不就是找找循环节嘛,因为如果存在循环节,那么一定会造成重复,比如123123123,循环节是3,所以只需要循环数字三次即可,再多循环就会造成重复,那么问题就来了,怎么计算循环节呢?在学习kmp中,我们利用fail数组的功能,很容易计算得到循环节。证明我这就不了,只说下方法,就是s.size()-fail[s.size()],当然这得要s.size()能整除前面那个式子才能是循环节。找到循环节了,又该怎么办呢,该怎么比较呢,不妨先列下来看看:

假设该数为:21213,那么下面就是他的悬转数字。

32121

13212

21321

12132

21213

正着看不好看,那么就倒着看,咦~,这不就是扩展kmp嘛。前后缀进行比较啦。从前后缀初出现不相等的第一个元素开始比较。

好啦,现在开始写程序啦。

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=1e5+10;

int Next[maxn];
int extand[maxn];       //s的后缀与t的最长公共前缀。
int fail[maxn];

void get_fail(char *pattern)
{
    int k=-1;
    fail[0]=-1;
    int len=strlen(pattern);
    for(int i=1;i<len;i++)
    {
        while(k>-1&&pattern[k+1]!=pattern[i]) k=fail[k];
        if(pattern[k+1]==pattern[i])
            k++;
        fail[i]=k;
    }
}

void getnext(char* t)
{
    int i,len=strlen(t);
    Next[0]=len;
    for(i=0;i<len-1&&t[i]==t[i+1];i++);
    Next[1]=i;
    int a=1;
    for(int k=2;k<len;k++){
        int p=a+Next[a]-1,l=Next[k-a];
        if(k+l-1>=p){       //l>=p-k+1
            int j=max(p-k+1,0);
            while(k+j<len&&t[k+j]==t[j]) j++;
            Next[k]=j;a=k;
        }else Next[k]=l;
    }
}

int main()
{
    char s[maxn],t[maxn];
    while(scanf("%s",s)!=EOF){
        getnext(s);
        get_fail(s);
        int len=strlen(s);
        int tmp=len-fail[len-1]-1;
        int flag;
        if(len%tmp==0) flag=tmp;
        else flag=len;
        int same=0,da=0,xiao=0;
        for(int i=0;i<flag;i++){
            if(Next[i]==len) same++;
            else if(s[Next[i]+i]>s[Next[i]]) da++;
            else if(s[Next[i]+i]<s[Next[i]]) xiao++;
        }
        printf("%d %d %d\n",xiao,same,da);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40679299/article/details/82018060