大部分内容整理自here
http://blog.youkuaiyun.com/zy691357966/article/details/39854359
求字符串的循环最小表示
https://www.luogu.org/problem/show?pid=1709
算法流程
我们设置两个指针i,j;设置变量k表示以i,j为开始,两字符串相同的长度;采取滑动的方式,在On的时间内实现求解
1.
初始化两个指针 i=0,j=1;
2.
k=0开始,当while(s[i+k]==s[j+k]) k++;
,直到找到第一个不同(若k已经是整个字符串的长度也为找到不同,那么较前位置的指针就是ans)
在该过程中,s[i+k]和s[j+k]有3种情况
假设证明时i < j
(A) if(s[i+k]>s[j+k]) i=i+k+1;
即s[i]–s[i+k]不会是最小表示的前缀
证明:
设一ii在s[i]--s[i+k]中
则必定能在s[j]--s[j+k]中找到对应的jj使得s[jj]=s[ii]
因为s[i]-s[i+k-1]==s[j]-s[j+k-1]
所以s[jj]-s[j+k-1]==s[ii]-s[i+k-1]这两个子串是相等的,又因为s[j+k]<s[i+k]所以以ii开始的字符串大于以jj开始的字符串,
即s[i]--s[i+k]不会是最小表示的前缀
(B)if(s[i+k]<s[j+k]) j=j+k+1
即s[j]–s[j+k]不会是最小表示的前缀
证明过程同上
证明:
设一jj在s[j]-s[j+k]中
则必定能在s[i]--s[i+k]中找到对应的ii使得s[ii]=s[jj]
因为s[i]-s[i+k-1]==s[j]-s[j+k-1]
所以s[ii]-s[i+k-1]==s[jj]-s[j+k-1]这两个子串是相等的,又因为s[i+k]<s[j+k]所以ii开始的字符串小于以jj开始的字符串,
即s[j]--s[j+k]不会是最小表示的前缀
(C)while(s[i+k]==s[j+k]) k++;
当k==len时,返回答案
注意:如果滑动时i,j处于同一个位置,将j++;
3.
当k=len时,返回i,j中较小的一个
当i>= len,返回j
当j>=len,返回i
注意:实际运行中,i,j的前后位置是不确定的,所以要返回其中较小的那个
i,j 都可能存有ans 两者互相更新,直到有一个更新后超过了len(包括len) 的时候 另一个即为正解
4.
进一步的优化,例如:i要移到i+k+1时,如果i+k+1 <= j的话,可以直接把i移到 j+1,因为,j到j+k已经检验过了该前缀比以i到i+k之间任何一个位前缀都小;j时的类似,移动到i+1。
不会证明
代码
未加优化
int Minn(char *s,int len){
int i=0,j=1,k=0;
while(i<len&&j<len){
k=0;
while(k<len&&s[i+k]==s[j+k]) k++;
if(k==len) return min(i,j);
if(s[i+k]<s[j+k]) j=j+k+1;
else i=i+k+1;
if(i==j) j++;
}
if(i>=len) return j;
else return i;
}
加上第四点优化
int Minn(char *s,int len){
int i=0,j=1,k=0;
while(i<len&&j<len){
k=0;
while(k<len&&s[i+k]==s[j+k]) k++;
if(k==len) return min(i,j);
if(s[i+k]>s[j+k]) {
if(i+k+1>j) i=i+k+1;
else i=j+1;
}
else {
if(j+k+1>i) j=j+k+1;
else j=i+1;
}
if(i==j) j++;//实际上此时已经不会出现这种情况
}
if(i>=len) return j;
else return i;
}
在洛谷上评测,加了优化比不加优化慢,也是很迷233