@(K ACMer)
题意:
求所给字符串的循环表示的最小字典序.
分析:
循环字符串的最小表示集裸题.
最朴素的方法是
O(n2)
的复杂度,显然超时.这里用了一个很巧妙的性质来剪枝,使复杂度降到
O(n)
.
字符串循环的最小独立集
这里引入同构的知识,我们有树的同构,图的同构,和字符串的同构.其所为同构是指他们在进行一些可取的等效操作(比如树和图的旋转,字符串的移位)后是相同的,即本质相同.
求一个可循环移位的字符串的最小字典表示.
用来判断两个字符串是否循环同构.(这里也可以用kmp算法
O(n)
的实现).
这里先很容易的提出暴力的算法:
int getmins(void) {
int i = 0, j = 1, k = 0;
while (k < l && i < l && j < l) {
if (s[j] < s[i]) i = j, j = i + 1;
else if (s[j] > s[i]) j++;
else {
k = 0;
while (k < l) {
int t = s[(j + k) % l] - s[(i + k) % l];
if (t > 0) {
j = j + 1; //替换1
break;
} else if (t < 0) {
i = j;
j = i + 1;
break;
} else {
k++;
}
}
}
}
return i;
}
这个暴力算法很明显是
O(n2)
的下届.但是这里我们可以有个非常强大的剪枝,观察上午代码中替换1的注释那一句.这里我们的j想替换i作为最小的其实位置失败了,所以我们就把
j++
来继续枚举下一个想来替换i的位置.但是我们发现没有其实在
[j,j+k]
范围内的所有其实位置都已经没有必要计算了它们都不可能比i为起始位置的字符串小,来替换以i为起始位置的字符串.所以我们可以直接把j替换为
(j+k+1)
位置来进行比较.
改进后的代码:
int getmins(void) {
int i = 0, j = 1, k = 0;
while (k < l && i < l && j < l) {
if (s[j] < s[i]) i = j, j = i + 1;
else if (s[j] > s[i]) j++;
else {
k = 0;
while (k < l) {
int t = s[(j + k) % l] - s[(i + k) % l];
if (t > 0) {
j = j + k + 1; //替换1
break;
} else if (t < 0) {
i = j;
j = i + 1;
break;
} else {
k++;
}
}
}
}
return i;
}
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int mod = int(1e9) + 7, INF = 0x3f3f3f3f, maxn = 1e5 + 40;
int T, l;
char s[maxn];
//令i表示当前最小串,j表示最小串的搜索指针.
int getmins(void) {
int i = 0, j = 1, k = 0;
while (k < l && i < l && j < l) {
if (s[j] < s[i]) i = j, j = i + 1;
else if (s[j] > s[i]) j++;
else {
k = 0;
while (k < l) {
int t = s[(j + k) % l] - s[(i + k) % l];
if (t > 0) {
j = j + k + 1; //替换1
break;
} else if (t < 0) {
i = j;
j = i + 1;
break;
} else {
k++;
}
}
}
}
return i;
}
int main(void)
{
scanf("%d", &T);
while (T--) {
getchar();getchar();
scanf("%s", s);
l = (int)strlen(s);
int maxs = getmins();
for (int i = maxs, cnt = 0; cnt < l; cnt++, i++) {
if (i == l) i = 0;
printf("%c", s[i]);
}
printf("\n");
}
return 0;
}