题意:
有一个字符串,它可以通过左移一位变成另外一个同构字符串,每产生一个新的同构字符串,就编号加一,现在要你输出四个值:
最小字典序的编号,最小字典序个数,最大字典序编号,最大字典序个数。
题解:
一开始我的想法就是暴力求出最小字典序和最大字典序,结果估算了一下喜闻乐见的超时了,然后我去看了一下,原来还有最小最大表示法这种黑科技的ORZ,突然感觉自己菜的不单单是这些常用的算法还有其他算法的知识面。。。然后用最小最大表示法算出最小最大字典序之后就可以用KMP来判断是否循环来得到字典序个数了,为什么可以用KMP判断循环来得到字典序个数呢?因为如果你的字典序如果还会出现一次的话说明肯定是构成了循环,那么我们就可以用KMP来判断循环了。然后就是输出结果了。我虽然过了但是我之前的做法是将最小最大字典序都存起来跑两遍next数组的因为我之前以为他们变换位置之后就会构成循环,next的数值就会改变,后来我在纸上画了一下发现我想多了,next的数值的确改变了,但是你左移字符串并没有改变字符串原本是否循环的特性,该循环还是循环,不该循环的还是不循环的。所以安心的用原数组来判断就可以了,最大最小字典序的个数都是一样的,因为是同一个字符串。
之前我多虑的做法
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=1000000+7;
char s[MAXN];
char c[MAXN];
int Next[MAXN];
int get_min()
{
int slen=strlen(s);
int i=0,j=1,k=0;
while(i<slen&&j<slen&&k<slen)
{
int t=s[(i+k)%slen]-s[(j+k)%slen];
if(t==0)
k++;
else
{
if(t>0)
i+=k+1;
else
j+=k+1;
if(i==j)
j++;
k=0;
}
}
return min(i,j);
}
int get_max()
{
int slen=strlen(s);
int i=0,j=1,k=0;
while(i<slen&&j<slen&&k<slen)
{
int t=s[(i+k)%slen]-s[(j+k)%slen];
if(t==0)
k++;
else
{
if(t>0)
j+=k+1;
else
i+=k+1;
if(i==j)
j++;
k=0;
}
}
return min(i,j);
}
void getNext()
{
Next[0]=-1;
int j=0,k=-1;
int clen=strlen(c);
while(j<clen)
{
if(k==-1||c[k]==c[j])
{
j++;
k++;
Next[j]=k;
}
else
k=Next[k];
}
}
int main()
{
while(~scanf("%s",s))
{
int minn=get_min(),maxx=get_max();
int slen=strlen(s);
for(int i=minn,j=0;i<minn+slen;i++)
{
c[j++]=s[i%slen];
}
// printf("%s\n",c);
getNext();
int L=slen-Next[slen];
int ans1=slen%L? 1:slen/L;
for(int i=maxx,j=0;i<maxx+slen;i++)
{
c[j++]=s[i%slen];
}
// printf("%s\n",c);
getNext();
L=slen-Next[slen];
int ans2=slen%L? 1:slen/L;
printf("%d %d %d %d\n",minn+1,ans1,maxx+1,ans2);
}
}
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=1000000+7;
char s[MAXN];
int Next[MAXN];
int get_min()
{
int slen=strlen(s);
int i=0,j=1,k=0;
while(i<slen&&j<slen&&k<slen)
{
int t=s[(i+k)%slen]-s[(j+k)%slen];
if(t==0)
k++;
else
{
if(t>0)
i+=k+1;
else
j+=k+1;
if(i==j)
j++;
k=0;
}
}
return min(i,j);
}
int get_max()
{
int slen=strlen(s);
int i=0,j=1,k=0;
while(i<slen&&j<slen&&k<slen)
{
int t=s[(i+k)%slen]-s[(j+k)%slen];
if(t==0)
k++;
else
{
if(t>0)
j+=k+1;
else
i+=k+1;
if(i==j)
j++;
k=0;
}
}
return min(i,j);
}
void getNext()
{
Next[0]=-1;
int j=0,k=-1;
int slen=strlen(s);
while(j<slen)
{
if(k==-1||s[k]==s[j])
{
j++;
k++;
Next[j]=k;
}
else
k=Next[k];
}
}
int main()
{
while(~scanf("%s",s))
{
int minn=get_min(),maxx=get_max();
int slen=strlen(s);
getNext();
int L=slen-Next[slen];
int ans=slen%L? 1:slen/L;//为什么寻找原字符串就可以了呢?因为如果该字符串是循环的话你再怎么变都还是循环的。
printf("%d %d %d %d\n",minn+1,ans,maxx+1,ans);
}
}