【笔记】字符串的最小表示法

大部分内容整理自here
http://blog.csdn.net/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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值