题意:
有一个 len 长度的环,问有没有字典序最大长度为 len 的串在 这个环里。
如果有的话,且只有一个 ,输出其开头的下标(下标从1 开始)
再输出0表示顺时针(从左至右),1 表示逆时针(从右至左)
如果多个,输出开头下标最小的那个。
如果顺时针,逆时针的字典序一样,且开头下标一样,优先输出输出顺时针。
解析:
参考了别人的题解,用最小表示法来做。
循环字符串的最小表示法的问题可以这样描述:对于一个字符串S,求S的循环的同构字符串S’中字典序最小的一个。
先给出最小表示法和最大表示法的模板:
int getMinString(char s[]) {
int len = strlen(s);
int i = 0, j = 1, k = 0;
while(i < len && j < len && k < len) {
int t = s[(i+k)%len] - s[(j+k)%len];
if(t == 0) k++;
else {
if(t > 0) i += k + 1;
else j += k + 1;
if(i==j) j++;
k = 0;
}
}
return min(i,j);
}
int getMaxString(char s[]) {
int len = strlen(s);
int i = 0, j = 1, k = 0;
while(i < len && j < len && k < len) {
int t = s[(i+k)%len] - s[(j+k)%len];
if(t == 0) k++;
else {
if(t > 0) j += k + 1;
else i += k + 1;
if(i==j) j++;
k = 0;
}
}
return min(i,j);
}
对于正序的字符串可以直接用最大表示法 ,可以得到最大字典序,以及最小的开头位置的下标。
对于逆序就把串翻转一下,用最大表示法,可以得到最大字典序, 也是下标最小开头位置的下标,但是因为颠倒了, 这里的下标是倒序的,所以下标其实是最大的。
但是我们已经得到最大字典序的串了, 所以 接下来把反的串延长一倍,可以利用
kmp算法
找其中相同串的最大起始位置,其镜像位置就是最小的位置。
然后把两个 最大字典序的串 比较下大小。
正序大输出正序,逆序大输出逆序,相等则输出标号最小的。
my code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = (int)2e4 + 10;
char str[N], save[N*2];
char str0[N], str1[N];
int pos0, pos1;
int len;
//最大表示法
int getMaxString(char s[]) {
int len = strlen(s);
int i = 0, j = 1, k = 0;
while(i < len && j < len && k < len) {
int t = s[(i+k)%len] - s[(j+k)%len];
if(t == 0) k++;
else {
if(t > 0) j += k + 1;
else i += k + 1;
if(i==j) j++;
k = 0;
}
}
return min(i,j);
}
int Next[N];
void getNext(char s[]) {
int len = strlen(s);
int j = 0, k = -1;
Next[0] = -1;
while(j < len) {
if(k == -1 || str[j] == str[k])
Next[++j] = ++k;
else k = Next[k];
}
}
int kmp(char tar[], char pat[]) {
getNext(pat);
int res = 0;
int lenT = strlen(tar), lenP = strlen(pat);
int j = 0, k = 0;
while(j < lenT - 1) {
if(k == -1 || tar[j] == pat[k])
j++, k++;
else k = Next[k];
if(k == lenP) {
res = max(res, j - lenP);
k = Next[k];
}
}
return lenP - res - 1;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d%s", &len, str);
pos0 = getMaxString(str);
for(int i = 0; i < len; i++) str0[i] = str[(i + pos0) % len];
str0[len] = '\0';
strcpy(save, str);
reverse(save, save + len);
pos1 = getMaxString(save);
for(int i = 0; i < len; i++) str1[i] = save[(i + pos1) % len];
str1[len] = '\0';
for(int i = 0; i < len; i++) save[i+len] = save[i];
save[len*2] = '\0';
pos1 = kmp(save, str1);
pos0++, pos1++;
int cmp = strcmp(str0, str1);
if(cmp > 0) {
printf("%d 0\n", pos0);
}else if(cmp < 0) {
printf("%d 1\n", pos1);
}else {
if(pos0 <= pos1) printf("%d 0\n", pos0);
else printf("%d 1\n", pos1);
}
}
return 0;
}