题目大意:给一个字符串,求它的循环最大表示开始位置,以及方向。
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; }