欢迎参观!!==>原文链接 <== 欢迎参观!!
欢迎参观!!==> 博客地址 <== 欢迎参观!!
字符串的最小表示法,就是对于一个字符串,可以将它的最后一位放到第一位来,依次类推,一共有n种变形,n为字符串长度
例如:
s = “00ab”
变形有 : b00a, ab00, 0ab0
一共4种
那么找到其中字典序最小的一个,用的算法便是这个。
思路:
定义三个指针,i, j, k,初始i=0;j=1;k=0。
首先:
-
如果 s[i] < s[j] 那么很明显 j++
-
如果 s[i]>s[j] 那么也很明显 i = j++
-
剩下的就是如果 s[i]==s[j] 的时候:
这时候有一个性质就是在i和j之间的所有的字符一定是大于等于s[i]的
另k=0,循环寻找第一个 s[i+k] != s[j+k] 的位置
如果 s[i+k] < s[j+k] 那么 j += k+1
为什么呢?
首先s[i]到s[i+k-1]一定是大于等于s[i],因为如果其中有一个数小于s[i],那么这个数一定在s[j]到s[j+k-1]中存在,又因为必定有一个会在后面,所以如果s[j]先碰到了,那么一定不会继续到k的位置的,所以一定不存在比s[i]小的字符。
所以从其中的任意一个字符开始当作起始点,都不会比现在更小,所以只有从选出来的序列的后面那一个字符开始才有可能会是最小。
所以 j + = k+1
如果序列中某个数和s[i]相等的话,那么一定会有之前或者以后再这个位置起始过,所以不需要再从这个位置进行起始。
因为在这里i和j是等价的,i在前和j在前的结果是一样的,所以i和j的处理是相同的,下面就不仔细的进行讲解了,直接贴代码:
还有就是如果i==j那么让j++就可以回到原先的状态了
最后的时候,肯定是小的不会动,而大的会不停的向后移动,所以最后只需要输出i和j最小的一个即可
/**
*字符串的最小表示法:
*
*循环同构
*字符串S = “dcba”,它的循环同构"cbad",“badc”,“abcd”.
*
*最小表示
*字符串和它的所有循环同构中字典序最小的字符串。S = “dcba"的最小表示"abcd”
*/
int minRepresent(char *s, int len) {
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 = (j+k+1>i+1) ? j+k+1:i+1;
else i = (i+k+1>j+1) ? i+k+1:j+1;
k = 0;
}
}
return (i<j) ? i:j;
}