http://acm.hdu.edu.cn/showproblem.php?pid=5442
给定一个字符串,长度为n。
定义有正和反两种读的方法,则其同构的串总共为2*n.
问你这么多串中 最大的同构串的起始位置。
若最大同构串出现多次。
①要求起始位置最小。(位置从1到n)
② 如果正向和反向的起始位置一样,则输出正向。
。
有kmp的方法,但是没怎么搞明白。
后缀数组。。我还没怎么看呢qwq
看了大佬的 用最小表示法做的。惊天为人qwq
① 正向的不用说了,直接用模板算。
关键是反向的!!,做法是求出该串的循环节,然后用长度减去循环节+min(i,j)。(i在匹配的过程中肯定是最小的那个??,如果匹配完成的话。。,因为我看别人的代码就是直接用的i。。)
! 最关键的就是用循环节反向求出 最大表示法的最大下标。
#include <bits/stdc++.h>
using namespace std;
/* 最小表示法求两次。
一次是正序的,一次是逆序的。
*/
const int maxn=1e5;
string s;
int ans1;
int ans2;
int MAXR(string s){
int len=s.length();
int i=0;int j=1;
//int k=0;
while(i<len/2&&j<len/2){
int k=0;
while(s[i+k]==s[j+k]&&k<len/2) k++;
if(k==len/2) return min(i,j);
if(s[i+k]<s[j+k])// 若求最大,把大于改成小于,下面也是。
i=max(i+k+1,j+1);
else if(s[i+k]>s[j+k])
j=max(j+k+1,i+1);
}
return min(i,j);
}
int MAXRR(string s){
int len=s.length();
int i=0;int j=1;
//int k=0;
while(i<len/2&&j<len/2){
int k=0;
while(s[i+k]==s[j+k]&&k<len/2) k++;
if(k==len/2) {
int ss=abs(i-j);//这是循环节
return len/2-ss+min(i,j);//别人代码直接+i。、、
}
if(s[i+k]<s[j+k])// 若求最大,把大于改成小于,下面也是。
i=max(i+k+1,j+1);
else if(s[i+k]>s[j+k])
j=max(j+k+1,i+1);
}
//int ss=abs(i-j);
//cout<<ss<<"xhj"<<endl;
return min(i,j);
}
int d;
string w1,w2;
void solve1(){
w1=s+s;
ans1=MAXR(w1);
}
void solve2(){
w2=s+s;
ans2=MAXRR(w2);
//cout<<ans2<<"???"<<endl;
}
int main()
{ int t;
scanf("%d",&t);
while(t--){
cin>>d;
cin>>s;
solve1();
string s1=w1.substr(ans1,d);
ans1++;
reverse(s.begin(),s.end());
solve2();
string s2=w2.substr(ans2,d);
//cout<<s1<<" "<<s2<<endl;
//cout<<ans1<<" "<<ans2<<endl;
if(s1>s2)
printf("%d 0\n",ans1);
else if(s2>s1)
printf("%d 1\n",d-ans2);
else if(s2==s1){
if(ans1>(d-ans2))
printf("%d 1\n",d-ans2);
else
printf("%d 0\n",ans1);
}
}
return 0;
}