只能说我刚学完最小表示法,现在写一下自己的感想,因为不懂怎么用csdn,所以只有文字描述和代码
#include
#include
#include
#include
#include
using namespace std;
///最小表示法
/*
最小表示法就是表示一个循环的字符串的最小表示
例如:
字符串cbabc的最小表示为abccb,就是从第3个字母开始
现在使用两个变量 u,v(无论如何确保 u < v)表示在比较的开始位置,字符串为s,长度为n
使用k表示从u和v开始的第k个位置的字母(每次比较k从0开始)
如果s[(u + k)%n] == s[(v + k)%n], 那么k++
如果k == n,那么直接返回u或则v
当s[(u + k)%n] < s[(v + k)%n] 时,那么就表示u开头的字母比较小 v就可以直接等于v + k + 1
为什么不能是v ~ v + k + 1之间的一个数呢,其实很简单,
因为当我们随意选择v ~ v + k + 1其中一个数为字符串的开始位置是时,
假设这个数为v + i,那么以u + i为开始的字符串必然小于v + i的字符串
而且u + i为开头的字符串也必然小于以u开头的字符串(
证:假设u + i <= v,那么我们可以知道 u < v,而且以u~v之间的数开头的字符串必然小于以u开头的字符串
因为如果中间有比u小的字符串,那么v是必然经过这个数的
当u + i > v时,你可以知道u + i的字符串必然小于u+i-(v-u)的字符串,而u+i-(v-u)的字符串又小于u的字符串
所以 v就可以直接等于v + k + 1
当s[(u + k)%n] > s[(v + k)%n] 时,我们可以知道v的字符串比较小
因为以u~v之间的一个数开头的字符串必然小于以u开头的字符串,而u的字符串小于v的字符串
所以如果u + k + 1 <= v, 那么u 直接可以等于v + 1
同理,如果u + k + 1 > v,那么u 直接可以等于u + k + 1
以上
*/
const int M = 1e5 +5;
char s[M];
int n;
int min_string(){
int u = 0, v = 1;//表示比较的串的开始位置
while(u < n && v < n){
int k = 0;
while(k < n && s[(u + k)%n] == s[(v + k)%n]) k++;
if(k == n) return u;
if(s[(u + k)%n] < s[(v + k)%n]) v = v + k + 1;
else{
if(u + k + 1 <= v) u = v + 1;
else u = u + k + 1;
}
if(u > v) swap(u,v);
}
return u;
}
//
int main(){
int _;
scanf("%d",&_);
while(_--){
scanf("%d%s",&n,s);
printf("%d\n",min_string());
}
return 0;
}