题目大意:有一个字符串长度为N的字符串,这个字符串可以扩展出N个字符串,并且按照顺序编号,比如串
”
SKYLONG
“
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
下面这7个都是原串的扩展(循环位移),现在需要求出来字典序最小的和字典序最大的那个串的标号。
输出说明:最小字典序的编号,最小字典序个数,最大字典序编号,最大字典序个数。
分析:以前也遇到过类似的求最小字典序的问题,不过当时不会,今天研究了一下发现是有个方法来专门处理这种问题的,就是最大最小表示
,这种方法采用两个指针,表示两个串的开头,如果开头不同直接让字典序大的后移,如果开头相同那么就使用一个计数长度k来往后移动,知道发现s[i+k] != s[j+k] 当然如果k==N那么这两个串都是最小的字典序了,否则,就让值大的那个指针往后移动,直到有指针超过N为止。
下面是代码:
==========================================================================================================
#include<stdio.h> #include<string.h> #include<algorithm> #include<stdlib.h> using namespace std; const int MAXN = 1e6+7; char s[MAXN<<1], s1[MAXN]; int Next[MAXN]; void GetNext(char s[], int N) { int i=0, j=-1; Next[0] = -1; while(i < N) { if(j==-1 || s[i]==s[j]) Next[++i] = ++j; else j = Next[j]; } } int GetMin(char s[], int N) {///求最小的字典序的开始 int i=0, j=1; while(i<N && j<N) { int k = 0; while(s[i+k] == s[j+k] && k<N) k++; if(k == N)break; if(s[i+k] < s[j+k]) { if(j+k > i) j = j+k+1; else j = i+1; } else { if(i+k > j) i = i+k+1; else i = j+1; } } return min(i, j); } int GetMax(char s[], int N) {///求最大的字典序的开始 int i=0, j=1; while(i<N && j<N) { int k = 0; while(s[i+k] == s[j+k]) k++; if(k == N)break; if(s[i+k] > s[j+k]) { if(j+k > i) j = j+k+1; else j = i+1; } else { if(i+k > j) i = i+k+1; else i = j+1; } } return min(i, j); } int main() { while(scanf("%s", s1) != EOF) { int N = strlen(s1); strcpy(s, s1); strcat(s, s1); GetNext(s, N); int circle = N-Next[N], times=1; if(N % circle == 0) times = N / circle; printf("%d %d %d %d\n", GetMin(s, N)+1, times, GetMax(s, N)+1, times); } return 0; }
时间: 2024-10-06 00:53:21