首页 > 代码库 > 【转载】字符串最小表示法-O(n)算法

【转载】字符串最小表示法-O(n)算法

原博客链接:http://blog.csdn.net/zy691357966/article/details/39854359

未授权,侵权删。

因为这篇博客写得真好。。转载了。。

红色的字是原博主写的,蓝色的字是我加的。

---------------------------------------------------------------------------------------------------------------------------------------------------------

这篇文章里的p1就是i,p2就是j

网上看了这篇文章后还是感觉有些地方讲的没有详细的证明所以添加了一点 红色字是博主写的

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

 

上面说的两个字符串同构的,并没有直接先求出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 的值。(这里的字符串是从0开始的,所以末位是len-1)

 

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

 

      如果 i >= len   则返回j(从1开始 : i>=len+1)

 

      如果 j >= len   则返回i (从1开始: j>=len+1)

如果看了上一篇文章 很容易对这里的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。

 这个优化就无需解释了

如i移到i+k+1,且i+k+1<=j,则j在i的后面,设j前一次位置为j‘,每一次跳的时候都保证了j‘~j-1这一段任意一个字符开始的子串都不可能,又因为j本来等于i+1,就是i+1到j-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 } 

 

 

我也附上我的代码:

 1 int Min() 2 { 3     int i=1,j=2,k; 4     while(i<=sl && j<=sl) 5     { 6         k=0;//debug 每次清零! 7         while(s[i+k]==s[j+k] && k<sl)//debug  8         { 9             k++;10             if(k==sl) return minn(i,j);11         }12         if(s[i+k]>s[j+k])13         {14             if(i+k+1<=j) i=j+1;15             else i=i+k+1;16         }17         else18         {19             if(j+k+1<=i) j=i+1;20             else j=j+k+1;21         }22         23     }24     if(i<=sl) return i;25     if(j<=sl) return j;26 }27 28 int Max()29 {30     int i=1,j=2,k;31     while(i<=sl && j<=sl)32     {33         k=0;34         while(s[i+k]==s[j+k] && k<sl)//debug k<sl35         {36             k++;37             if(k==sl) return minn(i,j);38         }39         if(s[i+k]<s[j+k])40         {41             if(i+k+1<=j) i=j+1;42             else i=i+k+1;43         }44         else45         {46             if(j+k+1<=i) j=i+1;47             else j=j+k+1;48         }49     }50     if(i<=sl) return i;51     if(j<=sl) return j;52 }

 

【转载】字符串最小表示法-O(n)算法