zoukankan      html  css  js  c++  java
  • 最小表示法 详解+模板+例题

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

    比如说一个字符串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
    ",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
    ");
        printf("%s",s1.a);
        return 0;
    }
    View Code
  • 相关阅读:
    文件系统操作与磁盘管理
    文件打包与解压缩
    环境变量与文件查找
    Linux目录结构及文件基本操作
    vim3
    vim2
    vim1
    用户管理
    初识
    第一章
  • 原文地址:https://www.cnblogs.com/cervusy/p/9972846.html
Copyright © 2011-2022 走看看