1. 程式人生 > >最小表示法 詳解+模板+例題

最小表示法 詳解+模板+例題

最小表示法是求與某個字串迴圈同構的所有字串中,字典序最小的串是哪個。

比如說一個字串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