最小表示法 详解+模板+例题
最小表示法是求与某个字符串循环同构的所有字符串中,字典序最小的串是哪个。
比如说一个字符串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 }
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; }