最小表示法 详解+模板+例题

最小表示法是求与某个字符串循环同构的所有字符串中,字典序最小的串是哪个。

比如说一个字符串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

 

posted @ 2018-11-17 09:13  cervusky  阅读(1851)  评论(1编辑  收藏  举报

Contact with me