链接:http://acm.hdu.edu.cn/showproblem.php?pid=3374
题意:一个长度为L的字符串,经过循环左移可以得到编号为1,2...L个字符串。问这L个字符串中字典序最大和最小的编号以及它们出现的次数。
分析:KMP+最小最大表示。如果这个字符串有循环节且循环节长度为T,那么字符串每循环左移T个字符得到的字符串和原串就相同。因此可以用KMP的next[]数组求出循环串的循环节长度,那么L/T就是每个不同循环串经过循环左移出现的次数。
对于如何快速求出一个字符串经过循环左移得到的字符串中字典序最大和最小的编号可用字符串的最大最小表示来求。具体参见这里:http://blog.csdn.net/hrhacmer/article/details/8909926
Source Code:
#include<cstring>
#include<cstdio>
#define maxn 1000010
#define Min(a,b) ((a)<(b)?(a):(b))
char str[maxn];
int next[maxn];
void GetNext(int l){
int i=0,j=-1;
next[0]=-1;
while(i<l){
if(j==-1||str[i]==str[j]){
i++,j++;
next[i]=j;
}
else j=next[j];
}
}
int MinnumRepresentation(int l){
int i=0,j=1,k=0,t;
while(i<l&&j<l&&k<l){
t=str[(i+k)%l]-str[(j+k)%l];
if(!t) k++;
else {
if(t>0) i=i+k+1;
else j=j+k+1;
if(i==j) j++;
k=0;
}
}
return Min(i,j);
}
int MaxnumRepresentation(int l){
int i=0,j=1,k=0,t;
while(i<l&&j<l&&k<l){
t=str[(i+k)%l]-str[(j+k)%l];
if(!t) k++;
else {
if(t<0) i=i+k+1;
else j=j+k+1;
if(i==j) j++;
k=0;
}
}
return Min(i,j);
}
int main()
{
while(~scanf("%s",str)){
int len=strlen(str);
int mapos=MaxnumRepresentation(len);
int mipos=MinnumRepresentation(len);
GetNext(len);
int tim=len-next[len];
if(len%tim) tim=1;
else tim=len/tim;
printf("%d %d %d %d\n",mipos+1,tim,mapos+1,tim);
}
return 0;
}