字符串的最小表示法

现在有个问题需要我们去解决,这个问题是这样的:

有一个字符串,这个字符串的首尾是连在一起的,要求寻找一个位置,使得以该位置为起点的字符串的字典序在所有的字符串中最小

最最暴力的做法就是用O(n)的时间枚举起始位置,O(n)的时间比对字符串的字典序,总的时间复杂度是O(n2),别想了,这种做法肯定不行。

那我们再来看一个稍微朴素点的算法(看的时候最好自己在草稿纸上模拟一下

我们设计ij两个指针。其中i指向最小表示的位置,j作为比较指针。

初始时令i = 0,j = 1 

如果S[i] > S[j] 则= j++

如果S[i] < S[j] 则j++

如果S[i]==S[j] 设指针k,分别从ij位置向下比较,直到S[i+k] != S[j+k]    

  如果S[i+k] S[j+k] 则= j++ 

  否则 j += k+1

返回

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

初始时令i=0,j=1 

如果S[i] > S[j] 则= j= i+1 

如果S[i] < S[j] 则j++

如果S[i]==S[j] 设指针k,分别从ij位置向下比较,直到S[i+k] != S[j+k]    

  如果S[i+k] > S[j+k] i += k+1

  否则j += k+1

返回ij的较小者

 

注意到上面两个算法唯一的区别是我用荧光笔刷出的那一行。这一行就把复杂度降到O(n)了。

为什么会这样呢?其实我开始看半天也不知道为什么会这样,我后来测试了一下,对于同一道题目,第使用一种算法用了625ms,而第二种只用了16ms!!你就知道差别有多大了。

按我的理解来说,第一种算法仅仅是用i维护了最小表示的位置,j不断往后进行比较;而第二种算法是比较完S[i+k]S[j+k]后根据情况来判断ij谁才是最小表示位置,对应的另外一个值去进行更新比较,这样ij都能用来维护了最小表示的位置,效率会高上很多,妙啊~这种思想好像在哪儿见过,具体的已经忘了,不过以后碰到了理解起来也会更加容易

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

Code

int Get_min()
{
    int n = strlen(s);
    int i = 0, j = 1, k = 0, t;
    //表示从i开始k长度和从j开始k长度的字符串相同
    while(i<n && j<n && k<n) {
        t = s[(i+k)%n] - s[(j+k)%n];
        //t用来计算相对应位置上那个字典序较大
        if(!t) k++;//字符相等的情况
        else {
            if(t>0) i += k+1; //i位置大
            else j += k+1; //j位置大
            if(i==j) j++;
            k = 0;
        }
    }
    return i >j ?j :i;
}
View Code

 参考文章:

https://blog.csdn.net/li1615882553/article/details/80136776

https://wenku.baidu.com/view/afcc12d4b14e852458fb571b.html

https://www.cnblogs.com/XGHeaven/p/4009210.html

https://www.cnblogs.com/justPassBy/p/5296275.html

https://www.cnblogs.com/sweat123/p/4723265.html

posted @ 2019-08-14 17:52  sparkyen  阅读(353)  评论(0编辑  收藏  举报