【题意】给出n(<=20000)长的循环字符串,求正序和逆序中的循环中n长的字符串字典序最大的开始位置,首先使得开始位置下标最小(下标1开始),如果最小起始位置相同选择正序的;最后输出最小起始位置和正反序(0正序,1逆序)。
【分析】其实还是很容易的,网络赛最后2小时打的有点晕了,竟然没看这题。
首先 ,对于正序的最小位置只要直接跑最大表示法即可。而对于逆序的,把原串翻转,然后跑最大表示法求出的位置是翻转以后的起始位置,则原串的起始位置只要用原串长度减去当前起始位置即可,但是这样求出的位置是原串中最后一个位置,我们要求的是最小的位置,可以用KMP跑一边求最大位置就是原来的最小位置。
这样时间复杂度为O(n)
【AC CODE】15ms
#include <bits/stdc++.h> using namespace std; typedef long long LL; #define rep(i,a,n) for(int i = a; i < n; i++) #define repe(i,a,n) for(int i = a; i <= n; i++) #define per(i,n,a) for(int i = n; i >= a; i--) #define clc(a,b) memset(a,b,sizeof(a)) const int INF = 0x3f3f3f3f, MAXN = 20000+10; int get_max(char*s, int len, char *ans)//ans为最小表示后的串,不输入就不求 { int i = 0,j = 1,k = 0,tmp; while(i < len && j < len && k < len) { tmp = s[(i+k)%len] - s[(j+k)%len]; if(!tmp) k++; else { if(tmp < 0) i += k+1;//改成<就是最大表示法 else j += k+1; if(i == j) j++; k = 0; } } int st = min(i, j);//st-最小表示法在原串起始位置 rep(i,0,len) ans[i] = s[(st+i)%len]; ans[len] = 0; return st; } char a[MAXN<<1],b[MAXN<<1],s1[MAXN],s2[MAXN]; int nt[MAXN];//nt[i]为满足x[i-z…i-1]=x[0…z-1]的最大z值(就是相同的最大前缀后缀) void get_next(char *x, int m)//这个nt[i]表示nt’[i] = nt[nt[…nt[i]]] { //直到nt'[i] < 0 或者x[nt’[i]] != x[i],这样要快一些 int i = 0,j; j = nt[0] = -1; while(i < m) { while(~j && x[i] != x[j]) j = nt[j]; if(x[++i] == x[++j]) nt[i] = nt[j]; else nt[i] = j; } } //返回x在y中出现的次数,可以重叠 int kmp(char *x, int m, char *y, int n)//x是模式串,y是主串 { int i = 0,j = 0,ans = 0; get_next(x,m); while(i < n) { while(~j && y[i] != x[j]) j = nt[j]; i++,j++; if(j == m) { ans = max(ans,i%m); j = nt[j];//改为j=0,则计算不重复数量 } } return ans; } int main() { #ifdef SHY freopen("d:\\1.txt", "r", stdin); #endif int t; scanf("%d", &t); while(t--) { int n; scanf("%d %s", &n,a); rep(i,0,n) b[n-i-1] = a[i]; rep(i,0,n) a[i+n] = a[i],b[i+n] = b[i]; int st1 = get_max(a,n,s1),st2 = get_max(b,n,s2); st2 = kmp(s2,n,b,n*2); int ans = st1,f = 0; int d = strcmp(s1,s2); if(d < 0) ans = n-st2-1,f = 1; else if(0 == d && n-st2-1 < st1) ans = n-st2-1,f = 1; printf("%d %d\n", ans+1,f); } return 0; } /* 4 4 dabc 4 abab 4 aaab 8 abcdabcd */