题目大意:给一个字符串,求它的循环最大表示开始位置,以及方向。
1、可用最大表示法求出顺时针的最大表示的最小开始位置,记为p1。利用该位置求出顺时针的最大表示字符串,记为s1
2、然后将字符串倒置,再用一次最大表示法。注意此时求出来的位置p其实是下标最大的开始位置p2,即p2=n-(p+1)+1。但可利用该位置求出逆时针的最大表示字符串,记为s2
3、将倒置的字符串扩充至2倍长度,利用KMP算法求出s2在该字符串中的最后出现位置p(即原串中的最小开始位置),即原串中的位置为n-(p+1)+1
4、比较p2和n-(p+1)+1,p2取较小值。
5、比较s1、s2得到结果。
有关证明:点击打开链接
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int work(int len,char pat[]) //最大表示法
{
int i=0,j=1,k=0;
while(i<len && j<len && k<len)
{
int t = pat[(i+k)%len] - pat[(j+k)%len];
if(!t) k++;
else
{
if(t>0) j = j+k+1;
else i = i+k+1;
if(i == j) j++;
k = 0 ;
}
}
return i<j?i:j;
}
int Next[20005];
void getNext(char *T)
{
int i=0,j=-1;
Next[0]=-1;
while(T[i]!='\0')
{
if(j==-1||T[i]==T[j]) Next[++i]=++j;
else j=Next[j];
}
}
int kmp(char *S,char *T)
{
int i=0,j=0,ans=-1,l1=strlen(S),l2=strlen(T);
while(i<l1)
{
if(j==-1||S[i]==T[j]) ++i,++j;
else j=Next[j];
if(j>=l2){
if(i<l1) ans=max(ans,i-l2+1);
j=Next[j];
}
}
return ans;
}
char s[40005],rs[20005],s1[20005],s2[20005];
int main()
{
int n,T,p,p1,p2;
scanf("%d",&T);
while(T--)
{
scanf("%d%s",&n,s);
p=work(n,s),p1=p+1;
int cnt=0;
while(cnt<n)
{
s1[cnt++]=s[p];
p=(p+1)%n;
}
s1[cnt]='\0';
for(int i=0;i<n;++i) rs[i]=s[n-1-i];
rs[n]='\0';
p=work(n,rs),p2=n-(p+1)+1;
cnt=0;
while(cnt<n)
{
s2[cnt++]=rs[p];
p=(p+1)%n;
}
s2[cnt]='\0';
for(int i=0;i<n;++i) s[i]=s[i+n]=rs[i];
s[n+n]='\0';
getNext(s2);
p2=min(p2,n-kmp(s,s2)+1);
int flag=strcmp(s1,s2);
if(flag>0) printf("%d 0\n",p1);
else if(flag<0) printf("%d 1\n",p2);
else printf("%d %d\n",min(p1,p2),p1<=p2?0:1);
}
return 0;
}