字符串 最小表示算法

来源:互联网 发布:sql语句是什么 编辑:程序博客网 时间:2024/05/23 16:56

参考自http://blog.csdn.net/cclsoft/article/details/5467743


循环字符串的最小表示法的问题可以这样描述:

对于一个字符串S,求S的循环的同构字符串S’中字典序最小的一个。

       由于语言能力有限,还是用实际例子来解释比较容易:
         设S=bcad,且S’是S的循环同构的串。S’可以是bcad或者cadb,adbc,dbca。而且最小表示的S’是adbc。


         (1) 对于字符串循环同构的最小表示法,其问题实质是求S串的一个位置,从这个位置开始循环输出S,得到的S’字典序最小。

  一种朴素的方法是设计i,j两个指针。其中i指向最小表示的位置,j作为比较指针。

       令i=0,j=1
       如果S[i] > S[j] i=j, j=i+1
       如果S[i] < S[j] j++
       如果S[i]==S[j] 设指针k,分别从i和j位置向下比较,直到S[i] != S[j]
       如果S[i+k] > S[j+k] i=j,j=i+1
       否则j++
       返回i

       起初,我想在j指针后移的过程中加入一个优化。就是j每次不是加1,而是移动到l位置。其中,l>j且S[l]<=S[j]。但是,即使加入  这一优化,在遇到bbb…bbbbbba这样的字符串时复杂度将退化到O(n^2)。


(2) 注意到,朴素算法的缺陷在于斜体的情况下i指针的移动太少了。针对这一问题改进就得到了最小表示法的算法。最小表示法的算法思路是维护两个指针i,j。

       令i=0,j=1
       如果S[i] > S[j] i=j, j=i+1
       如果S[i] < S[j] j++
       如果S[i]==S[j] 设指针k,分别从i和j位置向下比较,直到S[i] != S[j]
        如果S[i+k] > S[j+k] i=i+k
        否则j++
              返回i和j的小者

        注意到上面两个算法唯一的区别是粗体的一行。这一行就把复杂度降到O(n)了。

       这里证明一下为什么 S[i+k] > S[j+k] i=i+k ,

       当s[i+k] > s[j+k] 时 i = i+k+1;

       这个证明可以转化为证明  从 i ~ i+k 开始 到 i+k结束的前缀 一定大于某个t   ( t  在 j~j+k 内 ) 开始的前缀 , 接下来开始证明。

       从i ~ i +k 上取一个 ti ,对于 S[ ti ..i+k ] , 这一个字串,显然可以在S[j ....j+k] 上找到一个更小的字串.

       例如字符串dbcdbcdbce来说

        0  1  2  3  4  5  6  7  8  9 

       d  b  c   d  b  c  d   b  c  a   i = 0 , i + k = 6 

       3  4  5  6   7  8  9  0  1  2

       d  b  c  d   b  c  a  d   b  c   j = 3 ,    j + k = 9

       显然0 ~ 5 上面的子串,一定小于下面的对应位置的子串

       所以上式成立

              值得一提的是,与KMP类似,最小表示法处理的是一个字符串S的性质,而不是看论文时给人感觉的处理两个字符串。
       应用最小表示法判断两个字符串同构,只要将两个串的最小表示求出来,然后从最小表示开始比较。剩下的工作就不用多说了。

#include <bits/stdc++.h>using namespace std;int MinimumRepresentation(char *s,int len){        int i=0,j=1,k=0; //i和j是两个进行比较的起始匹配位点,k是匹配长度        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) i += k+1;            else j += k+1;            if( i == j ) j++;//i和j一定要错开            k = 0;//匹配长度要重置为0        }    }    return  i < j ? i : j ;//因为字典序大的位置被后移了,所以较小的位置就是答案}






0 0
原创粉丝点击