最小表示法 詳解+模板+例題
阿新 • • 發佈:2018-11-17
最小表示法是求與某個字串迴圈同構的所有字串中,字典序最小的串是哪個。
比如說一個字串jdrakioi,它長為8,也就是說最多有八種迴圈同構的方法。
jdrakioi、drakioij、rakioijd、akioijdr、kioijdra、ioijdrak、oijdraki、ijdrakio。
這幾個串在原串上的開始位置分別是0,1,2,3,4,5,6,7。
預設從0開始比較方便,這一點之後也會再提到。
暴力方法很簡單,把所有的都列出來再排個序就行了,不再贅述。
暴力的時間複雜度是很高的,然而我們可以做到O(n)求出字典序最小的串的開始位置。
設i、j是兩個“懷疑是最小的位置”,比如說如果你比較到了jdrakioi的兩個i,你目前還不知道從哪個i開始的字串是最小的。
設k表示,從i往後數和從j往後數,有多少是相同的。
開始時先設i=0,j=1,k=0。
每次都對i+k、j+k進行一次比較。
發現i+k有可能大於字串長度n啊,怎麼辦呢?
首先想到將字串倍長:jdrakioijdrakioi。
但是這樣做很麻煩,而且倍長之後,前後兩段都是完全一樣的。
所以我們只需要取模n就好了:(i+k)%n。
這麼做就要求字串從0開始,如果從1開始的話,就有點麻煩了,還得+1-1什麼的,不如從0開始簡單明瞭。
比較完i+k和j+k,如果兩個字元相等,那麼顯然k++。
如果不相等,那麼哪邊比較大,哪邊就肯定不是最小的了,同時把k重置為0。
如果出現了i、j重合的情況,把j往後移動一位。
最後輸出i、j較小的那個就好了。
int getmin() { int i=0,j=1,k=0,t; while(i<n&&j<n&&k<n) { t=s[(i+k)%n]-s[(j+k)%n]; if(!t)k++; else { if(t>0)i+=k+1; else j+=k+1; if(i==j)j++; k=0; } }return i<j?i:j; }
然後接一道裸題:hdu 2609 How many
跑完最小表示法之後,拿個map檢查是否出現過就好了。
注意有多組輸入輸出。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<map> 5 using namespace std; 6 7 int n,len,ans; 8 9 struct str 10 { 11 char a[105]; 12 friend bool operator < (str q,str w) 13 { 14 for(int i=0;i<len;i++) 15 { 16 if(q.a[i]==w.a[i])continue; 17 return q.a[i]<w.a[i]; 18 } 19 return 0; 20 } 21 }; 22 23 void getmin(str &x) 24 { 25 int i=0,j=1,k=0,t; 26 while(i<len&&j<len&&k<len) 27 { 28 t=x.a[(i+k)%len]-x.a[(j+k)%len]; 29 if(!t)k++; 30 else 31 { 32 if(t>0)i+=k+1; 33 else j+=k+1; 34 if(i==j)j++; 35 k=0; 36 } 37 } 38 i=(i<j?i:j); 39 str buf; 40 for(int p=0;p<len;p++)buf.a[p]=x.a[(p+i)%len]; 41 x=buf; 42 } 43 44 map<str,bool>mp; 45 46 int main() 47 { 48 while(scanf("%d",&n)!=EOF) 49 { 50 ans=0; 51 mp.clear(); 52 for(int i=1;i<=n;i++) 53 { 54 str nw; 55 scanf("%s",nw.a); 56 len=strlen(nw.a); 57 getmin(nw); 58 if(mp[nw])continue; 59 else ans++,mp[nw]=1; 60 } 61 printf("%d\n",ans); 62 } 63 return 0; 64 }View Code
bzoj 1398 Vijos1382尋找主人 Necklace
還是裸題。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct str { char a[1000005]; }s1,s2; int len; void getmin(str &x) { int i=0,j=1,k=0,t; while(i<len&&j<len&&k<len) { t=x.a[(i+k)%len]-x.a[(j+k)%len]; if(!t)k++; else { if(t>0)i+=k+1; else j+=k+1; if(i==j)j++; k=0; } } i=(i<j?i:j); str buf; for(int p=0;p<len;p++)buf.a[p]=x.a[(p+i)%len]; x=buf; } int main() { scanf("%s",s1.a); scanf("%s",s2.a); len=strlen(s1.a); if(len!=strlen(s2.a))return printf("No"),0; getmin(s1); getmin(s2); for(int i=0;i<len;i++) if(s1.a[i]!=s2.a[i]) return printf("No"),0; printf("Yes\n"); printf("%s",s1.a); return 0; }View Code