字符串最小表示法

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

对于一个字符串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的性质,而不是看论文时给人感觉的处理两个字符串。
应用最小表示法判断两个字符串同构,只要将两个串的最小表示求出来,然后从最小表示开始比较。剩下的工作就不用多说了。

[cpp]  view plain  copy

  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就好了


代码:



   
   
  1. int getMin(char *s)
  2. {
  3. int i = 0, j = 1, l;
  4. int len = strlen(s);
  5. while(i < len && j < len)
  6. {
  7. for(l = 0; l < len; l++)
  8. if(s[(i + l) % len] != s[(j + l) % len]) break;
  9. if(l >= len) break;
  10. if(s[(i + l) % len] > s[(j + l) % len])
  11. {
  12. if(i + l + 1 > j) i = i + l + 1;
  13. else i = j + 1;
  14. }
  15. else if(j + l + 1 > i) j = j + l + 1;
  16. else j = i + 1;
  17. }
  18. return i < j ? i : j;
  19. }
  20. int getMax(char *s)
  21. {
  22. int len = strlen(s);
  23. int i = 0, j = 1, k = 0;
  24. while(i < len && j < len && k < len)
  25. {
  26. int t = s[(i+k)%len]-s[(j+k)%len];
  27. if(!t) k++;
  28. else
  29. {
  30. if(t > 0)
  31. {
  32. if(j+k+ 1 > i) j = j+k+ 1;
  33. else j = i+ 1;
  34. }
  35. else if(i+k+ 1 > j) i = i+k+ 1;
  36. else i = j+ 1;
  37. k = 0;
  38. }
  39. }
  40. return i < j ? i : j;
  41. }


代码:

#include <bits/stdc++.h>

using namespace std;

int getMin(string s)
{
    int len = s.length();
    int i = 0, j = 1, k = 0,t;
    while(i < len && j < len && k < len) {
        t = s[(i+k)>=len?i+k-len:i+k] - s[(j+k)>=len?j+k-len:j+k];
        if (!t) ++k;
        else{
            if (t > 0) i = i+k+1;
            else
                j = j + k + 1;
            if(i == j) ++j;
            k = 0;
        }
    }
    return (i<j?i:j);
}

void Pre(string s, int i)
{
    int len = s.length();
    cout << s.substr(i,len-i)<<s.substr(0,i)<<endl;
}

int main()
{
    string s = "abcd";
    string s2 = "cdab";
    int t1 = getMin(s);
    int t2 = getMin(s2);
    Pre(s,t1);
    cout <<endl;
    Pre(s2,t2);

    return 0;
}

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

对于一个字符串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的性质,而不是看论文时给人感觉的处理两个字符串。
应用最小表示法判断两个字符串同构,只要将两个串的最小表示求出来,然后从最小表示开始比较。剩下的工作就不用多说了。

[cpp]  view plain  copy

  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. }  


猜你喜欢

转载自blog.csdn.net/u014046022/article/details/82112303
今日推荐