为什么要写这个算法?
看了看NOIP考点 这好像是省选的知识点诶
我作为一个今年11月份考完NOIP就AFO的OIer,我看这干啥?
我们还是要把目标放长远一点,我也不敢打包票,我一生中不会遇到这个算法。
对吧?
而且,还能提高自己的信息学素养!多好。
那么这个方法是怎么来的呢?
我做一道题(下面会粘的练习题)时,发现了这个算法,去网上找博客,但是那个人讲得很迷,另一个人又讲得很长,我一想,这一定是一个很简单的算法,算了,我自己写吧!
于是就得到了接下来的这个方法,当然已经经过测试,没有问题。
那么,下面我们步入正题。
什么是最小表示法?
我们的对象是字符串(读者:???你不要胡说,我有女朋友的好吧!你对象才是字符串嘞!)
我们这里的字符串不是一般的字符串,而是可以进行轮换对称变换的字符串。
也就是说我们把”abcd”、”bcda”、”cdab”、”dabc”视为一个字符串。
那么对于一个这样的字符串,它的所有的形式的集合中,字典序最小的一个就是该字符串的最小表示法。
比如上面的那一个,最小表示法就是”abcd”
原理
我们知道,越靠左边的字符对字典序的影响越大
所以我们肯定优先考虑最左边的一个字符
我们又由轮换对称变换的定义,可以知道,我们一旦确定一个字符串的起始位置,整个字符串的形式就确定了。
所以我们的目标就求这个其实位置。
所以我们设置两个下标i和j
i=0,j=1
然后我们有一个字符串c
我们开始比较i位和j位的字符
如果c[i]<c[j]
那么很好,显然i不变,j++即可
如果c[i]>c[j]
那么j位显然比i位更优,所以让i=j,然后j++
下面就是重点了
如果c[i]==c[j]
怎么办呢?
(读者:那就判断他们各自后面一位呗!)
没错就是这样
我们再设置一个变量k
让它从1开始递增到n-1即可(再往下就重复了)
下面我们需要了解一个性质:
用k比较过的位置就不需要再把他们作为头进行比较了
这个自己体会一下,我这里不提供证明。(其实分类讨论一下即可)
代码
int getmin(char* c){
int n=strlen(c);
int i=0,k=0;
int j=1;
while(i<n&&j<n){
if(c[i]<c[j]){
j++;
}else if(c[i]>c[j]){
i=j++;
}else{
int fir=1;
for(k=1;k<n;k++){
if(c[(i+k)%n]<c[(j+k)%n]){
j=(j+k+1);fir=0;
break;
}else if(c[(i+k)%n]>c[(j+k)%n]){
i=j;fir=0;
j=(j+k+1);
break;
}
}
if(fir){
break;
}
}
}
return min(i,j);
}