1. 程式人生 > >【學習筆記】最小表示法

【學習筆記】最小表示法

  • 用途

            給定一個長度為n,可旋轉的字串環,求從哪個位置斷開的長度為n的字串字典序最小/大(以最小為例,最大同理)  

  • 演算法描述

    •      令i=0,j=1表示最小的字串可能的位置;
    •      找到一個k使得s[i+1]==s[j+1],s[i+2]==s[j+2] ,..., s[i+k-1]==s[j+k-1] ;
    •      但是s[i+k]!=s[j+k];
    •      如果此時s[i+k]<s[j+k],對於j' = j + 1 到 j + k 的位置一定不是最優解,因為i+j'-j位置的串一定比j'位置的更優且如果i+j'-j       位置的串更優一定會被i選到,那麼就j=j+k+1
    •     s[i+k]>s[j+k]就i=i+k+1,注意i==j時要再向後移動一位
  • 複雜度 

    •  每多匹配一位k,i或j移動的次數就會相應多一位,所以O(n)
  • bzoj1398 
    Vijos1382尋找主人 Necklace
     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<queue>
     6 #include<cmath>
     7 #include<vector>
     8 #include<stack>
     9 #include<map>
    10 #include<set
    > 11 #define Run(i,l,r) for(int i=l;i<=r;i++) 12 #define Don(i,l,r) for(int i=l;i>=r;i--) 13 #define ll long long 14 #define ld long double 15 #define inf 0x3f3f3f3f 16 #define mk make_pair 17 #define fir first 18 #define sec second 19 #define il inline 20 #define rg register 21 #define pb push_back 22 using namespace std; 23 const int N=2000010; 24 int n; 25 char s[N],t[N]; 26 int cal(char *S){ 27 int i= 0, j = 1 ,k; 28 while(i<n&&j<n){ 29 for(k=0;S[i+k]==S[j+k];k++); 30 if(S[i+k]<S[j+k]){j+=k+1;if(i==j)j++;} 31 else {i+=k+1;if(i==j)i++;} 32 } 33 return min(i,j); 34 } 35 int main(){ 36 freopen("bzoj1398.in","r",stdin); 37 freopen("bzoj1398.out","w",stdout); 38 scanf("%s%s",s,t); 39 n = strlen(s); 40 for(rg int i=0;i<n;i++)s[i+n] = s[i] , t[i+n] = t[i]; 41 int pos1 = cal(s); 42 int pos2 = cal(t); 43 int fg=1; 44 for(rg int i=0;i<n;i++){ 45 if(s[pos1+i]!=t[pos2+i]){fg=0;break;} 46 } 47 if(!fg){puts("No");} 48 else { 49 puts("Yes"); 50 for(rg int i=0;i<n;i++){ 51 putchar(s[pos1+i]); 52 } 53 } 54 return 0; 55 }//by tkys_Austin;
    bzoj1398