您的位置:首页 > 其它

HDU - 4333 Revolving Digits(拓展kmp+最小循环节)

2015-11-06 20:35 337 查看
1、给一个数字字符串s,可以把它的最后一个字符放到最前面变为另一个数字,直到又变为原来的s。求这个过程中比原来的数字小的、相等的、大的数字各有多少。

例如:字符串123,变换过程:123 -> 312 -> 231 -> 123

因为:312>123, 231>123, 123=123

所以答案是:0 1 2

2、令str1=s,str2=s+s,然后str1作为子串,str2作为主串,进行扩展kmp求出str2[i...len2-1]与str1[0...len1-1]的最长公共前缀。当公共前缀==len1时,两个数相等;否则,只须比较公共前缀后的下一个字符就能判断大小了。

注意当中有重复的情况,只有当s有循环节的时候才会出现,先求出s的最小循环节,然后用s的长度除以最小循环节得到循环节的个数,将3个结果都除以循环节个数即可。

3、

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;

#define MaxSize 200005

int _next[MaxSize],extend[MaxSize];

//扩展kmp
//next[i]:x[i...m-1]与x[0...m-1]的最长公公前缀
//extend[i]:y[i...n-1]与x[0...m-1]的最长公共前缀
void pre_EKMP(char x[],int m,int _next[]){//m长度
_next[0]=m;
int j=0;
while(j+1<m&&x[j]==x[j+1])j++;
_next[1]=j;
int k=1;
for(int i=2;i<m;i++){
int p=_next[k]+k-1;
int L=_next[i-k];
if(i+L<p+1)_next[i]=L;
else{
j=max(0,p-i+1);
while(i+j<m&&x[i+j]==x[j])j++;
_next[i]=j;
k=i;
}
}
}

void EKMP(char x[],int m,char y[],int n,int _next[],int extend[]){//x子串,m子串长度,y主串,n主串长度
pre_EKMP(x,m,_next);
int j=0;
while(j<n&&j<m&&x[j]==y[j])j++;
extend[0]=j;
int k=0;
for(int i=1;i<n;i++){
int p=extend[k]+k-1;
int L=_next[i-k];
if(i+L<p+1)extend[i]=L;
else{
j=max(0,p-i+1);
while(i+j<n&&j<m&&y[i+j]==x[j])j++;
extend[i]=j;
k=i;
}
}
}
/*
子串  :a b a b
主串  :a b a b a c
next  :4 0 2 0
extend:4 0 3 0 1 0
*/
void GetNext(char t[]){//求next数组
int j,k,len;
j=0;//从0开始,首先求_next[1]
k=-1;//比较指针
_next[0]=-1;//初始值-1
len=strlen(t);
while(j<len){
if(k==-1||t[j]==t[k]){//指针到头了,或者相等
++j;
++k;
_next[j]=k;//此句可由优化替代
/*优化(求匹配位置时可用)
if(t[j]!=t[k])_next[j]=k;
else _next[j]=_next[k];
//*/
}
else k=_next[k];
}
}
int main(){
char str1[100005],str2[200005];//子串,主串
int i,j,len1,len2;//子串长度,主串长度
int T;
int sumL,sumE,sumG;
scanf("%d",&T);
for(i=1;i<=T;++i){
sumL=sumE=sumG=0;
scanf("%s",str1);
strcpy(str2,str1);
strcat(str2,str1);
len1=strlen(str1);
len2=strlen(str2);
EKMP(str1,len1,str2,len2,_next,extend);
for(j=0;j<len1;++j){
if(extend[j]==len1)++sumE;
else if(str2[j+extend[j]]<str1[extend[j]])++sumL;
else ++sumG;
}
GetNext(str1);
int repetend=len1-_next[len1];//最小循环节
int numR;//循环节的个数
if(len1%repetend==0)numR=len1/repetend;
else numR=1;
printf("Case %d: %d %d %d\n",i,sumL/numR,sumE/numR,sumG/numR);
}

return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: