Description:
Give you a string with length N, you can generate N strings by left shifts. For example let consider the string “SKYLONG”, we can generate seven strings:
String Rank
SKYLONG 1
KYLONGS 2
YLONGSK 3
LONGSKY 4
ONGSKYL 5
NGSKYLO 6
GSKYLON 7
and lexicographically first of them is GSKYLON, lexicographically last is YLONGSK, both of them appear only once.
Your task is easy, calculate the lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), its times, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
Input:
Each line contains one line the string S with length N (N <= 1000000) formed by lower case letters.
Output:
Output four integers separated by one space, lexicographically fisrt string’s Rank (if there are multiple answers, choose the smallest one), the string’s times in the N generated strings, lexicographically last string’s Rank (if there are multiple answers, choose the smallest one), and its times also.
Sample Input:
abcder
aaaaaa
ababab
Sample Output:
1 1 6 1
1 6 1 6
1 3 2 3
题意:一个长度为n的字符串,通过每次循环移位可以得到n个字符串,那么现在需要输出字典序最小的那个字符串出现的位置及其出现的次数,还有字典序最大的字符串出现的最早位置及其出现的次数,新的方法(最大最小表示)。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int N=1e6+10; char s[N*2], a[N]; ///s存放两个a字符串,供循环移位时需要 int Next[N], la; void Getnext() ///这个Next数组存放a的Next值 { int i = 0, j = -1; Next[0] = -1; while (i <= la) { if (j == -1 || a[i] == a[j]) { i++; j++; Next[i] = j; } else j = Next[j]; } } int GetMin() ///求出字典序最小的字符串最早出现的位置 { int i = 0, j = 1, k; ///这里i!=j,可以将s[i+k]和s[j+k]分别看做两个字符串的首字符(相当于在判断两个字符串的大小) while (i < la && j < la) { k = 0; ///k记录字符相等的个数 while (s[i+k] == s[j+k] && k < la) k++; if (k == la) break; ///若是la个字符都相等,那么可以确定两个字符串都是最小的,已经找到最小,停止查找 if (s[i+k] > s[j+k]) ///因为是求最小的,所以大的那个下标要后移,而小的值不变(类似求最小值,一直更新) { if (i+k > j) i = i+k+1; else i = j+1; } else { if (j+k > i) j = j+k+1; else j = i+1; } } return min(i, j); ///因为要最早,所以求最小值 } int GetMax() ///求出字典序最大的字符串最早出现的位置 { int i = 0, j = 1, k; while (i < la && j < la) { k = 0; while (s[i+k] == s[j+k] && k < la) k++; if (k == la) 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 () { int x, Time, Miid, Maid; ///x记录循环节的长度,Time记录出现的次数 while (scanf("%s", a) != EOF) { strcpy(s, a); strcat(s, a); la = strlen(a); Getnext(); x = la - Next[la]; Time = la / x; ///可以得出最小值和最大值出现次数都为循环节的个数 Miid = GetMin(); Maid = GetMax(); printf("%d %d %d %d\n", Miid+1, Time, Maid+1, Time); ///因为下标都是从0开始的,所以要加1 } return 0; }