Hidden Password -(超详细解释)字符串最小表示法 O(n)算法

网上看了这篇文章后还是感觉有些地方有点小错误,红色字是博主写的

求字符串的循环最小表示:

 

上面说的两个字符串同构的,并没有直接先求出Min(s),而是通过指针移动,当某次匹配串长时,那个位置就是Min(s)。而这里的问题就是:不是给定两个串,而是给出一个串,求它的Min(s),eg:Min(“babba”) = 4。那么由于这里并非要求两个串的同构,而是直接求它的最小表示,由于源串和目标串相同,所以处理起来既容易又需要有一些变化:我们仍然设置两个指针,i, j,其中i指向0,j指向1,仍然采用上面的滑动方式:

 

(1)  利用两个指针i, j。初始化时i指向0, j指向1。

 

(2)  k = 0开始,检验s[i+k] 与 s[j+k] 对应的字符是否相等,如果相等则k++,一直下去,直到找到第一个不同,(若k试了一个字符串的长度也没找到不同,则那个位置就是最小表示位置,算法终止并返回)。则该过程中,s[i+k] 与 s[j+k]的大小关系,有三种情况:

  证明的时候假设(i<j)的,无伤大雅 ;

     (A). s[i+k] > s[j+k],则i滑动到i+k+1处 — 即s1[i->i+k]不会是该循环字符串的“最小表示”的前缀。

 证明如下

    

     (B). s[i+k] < s[j+k],则j滑动到j+k+1处,原因同上。

 证明如下


     (C). s[i+k] = s[j+k],则 k++; if (k == len) 返回结果。 

    注:这里滑动方式有个小细节,若滑动后i == j,将正在变化的那个指针再+1。直到p1、p2把整个字符串都检验完毕,返回两者中小于 len 的值。

 

(3)   如果 k == len, 则返回i与j中的最小值

 

      如果 i >= len   则返回j

 

      如果 j >= len   则返回i

如果看了上一篇文章 很容易对这里的i,j 产生误会  误以为i为ans,j为比较指针

实际上这题中 i,j 都可能存有ans 两者互相更新,直到有一个更新后超过了len(包括len) 的时候 另一个即为正解

(4)   进一步的优化,例如:i要移到i+k+1时,如果i+k+1 <= p2的话,可以直接把i移到 j+1,因为,j到j+k已经检验过了该前缀比以i到i+k之间任何一个位前缀都小;j时的类似,移动到i+1。

 这个优化就无需解释了

至此,求一个字符串的循环最小表示在O(n)时间实现,感谢大牛的论文。其中实现时的小细节“如果滑动后p1 == p2,将正在变化的那个指针再+1”,开始没有考虑,害得我想了几个小时都觉得无法进行正确的移动。具体例题有两个:http://acm.zju.edu.cn 的2006和1729题。一个是10000规模一个是100000规模。运行时间前者是0S,后者是0.05S。



上自己丑陋的代码

      
      
  1. int MinimumRepresentation(int *s, int l)
  2. {
  3. int i,j,k;
  4. i= 0;j= 1;k= 0;
  5. while(i<l&&j<l)
  6. {
  7. k= 0;
  8. while(s[i+k]==s[j+k]&&k<l) k++;
  9. if(k==l) return i;
  10. if(s[i+k]>s[j+k])
  11. if(i+k+ 1>j) i=i+k+ 1;
  12. else i=j+ 1;
  13. else if(j+k+ 1>i) j=j+k+ 1;
  14. else j=i+ 1;
  15. }
  16. if(i<l) return i;
  17. else return j;
  18. }


之前被原来的博主的错误代码误导了,改了好久才发现。。。
更改的代码:
int min_rep(){//用最小表示法求字符串S的最小字典序,返回字典序最小的串的首字母位置

    int i = 1,j = 0,k = 0;
    while(i < n && j < n){//由于循环性,所以是str[(i+k)%n],要对n取模,保证不超过,变成循环的
    //也可也将原来的字符串*2 
        k = 0;
        while(str[(i+k) % n] == str[(j+k) % n] && k < n){//直到第一个不相等的位置 
            ++k;
        }
        if(k == n){
            return i < j?i :j;// 返回两者的较少值,而不是就是i
            //return min(i,j); 
            //return i;//这是错误的 
        }
        if(str[(i+k) % n] > str[(j+k) % n]){
            /*
            if(i +k +1 > j){
                i = i+k+1;  
            }else{
                i = j+1;
            }*/
            i = max(i+k+1 ,j + 1);//可以这样代替,简洁的写法. 
        }else{
            /*
            if(j+k+1 > i){
                j = j+k+1;  
            }else{
                j = i+1;
            }*/
            j = max(j+k+1, i+1);
        }
    }
    //return i < j? i :j; 
    return min(i,j);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值