首先得去重,怎么去重呢,刚开始想了好几种方法,都不太理想,后再才想明白,找重复的不就是找找循环节嘛,因为如果存在循环节,那么一定会造成重复,比如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;
}