1. 程式人生 > >字串最小/最大表示法 學習與總結

字串最小/最大表示法 學習與總結

迴圈字串的最小表示法的問題可以這樣描述:

對於一個字串S,求S的迴圈的同構字串S’中字典序最小的一個。

由於語言能力有限,還是用實際例子來解釋比較容易:
設S=bcad,且S’是S的迴圈同構的串。S’可以是bcad或者cadb,adbc,dbca。而且最小表示的S’是adbc。
對於字串迴圈同構的最小表示法,其問題實質是求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)。

注意到,樸素演算法的缺陷在於斜體的情況下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)了。
值得一提的是,與KMP類似,最小表示法處理的是一個字串S的性質,而不是看論文時給人感覺的處理兩個字串。
應用最小表示法判斷兩個字元串同構,只要將兩個串的最小表示求出來,然後從最小表示開始比較。剩下的工作就不用多說了。

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

然後主函式 設定變數 = 所求的, 迴圈輸出, 不斷++, %=n就好了

這是我看的部落格

然後自己總結出來 不就是一個比較的過程 維護兩個指標 哪個大讓哪個指標放後面 跳出時返回最小指標即可 也就是最小字串的開始位置 注意是0-n-1,不是1-n

演算法的優化就是讓之前很多相等的時候 直接右移大一點 因為你每次就算移1也不過是很多個過程實現了最終這一步

指標>len時候break 以及都是aaaaaaaaaaaaaaaaaaa也可以跳出比如迴圈串bcdabcda也可以跳出

模板

int getmin(string str)
    {
    int i = 0;
    int j = 1;
    int k = 0;
    int len = str.length();
    while (j < len && i < len && k < len)
    {
    if (str[(i + k) %len] == str[(j + k)%len ])
    {
    k++;
    }
    else
    {
    if (str[(i + k) %len] > str[(j + k) %len])
    {
    i = i + k + 1;
    }
    else
    {
    j = j + k + 1;
    }
    if (i == j)
    j++;
    k = 0;
    }
    }
    return i < j ? i : j;
    }
    
        int maxnum(char s[],int t1)
    {
        for(int i=0;i<t1;i++)
            s[i+t1]=s[i];
        int i=0,j=1;
        while(i<t1&&j<t1)
        {
            int k=0;
            while(s[i+k]==s[j+k]&&k<t1) k++;
            if(k==t1) break;
            if(s[i+k]<s[j+k]) i=max(i+k+1,j+1);
            else j=max(j+k+1,i+1);
        }
        return min(i,j);
    }