扩展kmp+最小循环节HDU 4333 Revolving Digits

Posted shulin~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了扩展kmp+最小循环节HDU 4333 Revolving Digits相关的知识,希望对你有一定的参考价值。

http://acm.hdu.edu.cn/showproblem.php?pid=4333

【题意】

  • 给定一个数字<=10^100000,每次将该数的第一位放到放到最后一位,求所有组成的不同的数比原数小的个数,相等的个数,大的个数

【思路】

  • 这个数很大,用字符串处理
  • 比较两个字符串的大小,一位一位很耗时,可以求出最长公共前缀,只比较最长公共前缀后一位
  • 每次将数的最后一位放到最后一位,如abcd变成dabc,cdab,bcda,相当于abcdabcd各个后缀的前四位
  • 这样就变成了求abcdabcd的每个后缀与abcd的最长公共前缀,用扩展KMP线性求
  • abab这种情况按上面的做法abababab会考察ab,ba,ab,ba,有重复的,是因为abab=(ab)^2,所以还要考虑最小循环节去重

【AC】

技术分享
  1 #include<iostream>
  2 #include<cstring>
  3 #include<string>
  4 #include<cstdio>
  5 #include<algorithm>
  6 using namespace std;
  7 typedef long long ll;
  8 const int maxn=2e5+3;
  9 char s[maxn];
 10 char t[maxn];
 11 int nxt[maxn];
 12 int extend[maxn];
 13 int nxtval[maxn];
 14 
 15 
 16 void pre_EKMP(char x[],int m,int nxt[])
 17 {
 18     nxt[0]=m;
 19     int j=0;
 20     while(j+1<m && x[j]==x[j+1]) j++;
 21     nxt[1]=j;
 22     int k=1;
 23     for(int i=2;i<m;i++)
 24     {
 25         int p=nxt[k]+k-1;
 26         int L=nxt[i-k];
 27         if(i+L<p+1) nxt[i]=L;
 28         else
 29         {
 30             j=max(0,p-i+1);
 31             while(i+j<m && x[i+j]==x[j]) j++;
 32             nxt[i]=j;
 33             k=i;
 34         }
 35     }
 36 }
 37 
 38 void EKMP(char x[],int m,char y[],int n,int nxt[],int extend[])
 39 {
 40     pre_EKMP(x,m,nxt);//子串 
 41     int j=0;
 42     while(j<n && j<m &&x[j]==y[j]) j++;
 43     extend[0]=j;
 44     int k=0;
 45     for(int i=1;i<n;i++)
 46     {
 47         int p=extend[k]+k-1;
 48         int L=nxt[i-k];
 49         if(i+L<p+1) extend[i]=L;
 50         else
 51         {
 52             j=max(0,p-i+1);
 53             while(i+j<n && j<m && y[i+j]==x[j]) j++;
 54             extend[i]=j;
 55             k=i;
 56         }
 57     }
 58 }
 59 
 60 void kmp_pre(char x[],int m,int nxtval[])
 61 {
 62     int i,j;
 63     j=nxtval[0]=-1;
 64     i=0;
 65     while(i<m)
 66     {
 67         if(j==-1||x[i]==x[j])
 68         {
 69             i++;
 70             j++;
 71             if(x[i]!=x[j]) nxtval[i]=j;
 72             else  nxtval[i]=nxtval[j];
 73         }
 74         else j=nxtval[j];
 75     }
 76 
 77 }
 78 
 79 void NextVal(char *T)
 80 {
 81     int i=0,j=-1;
 82     nxtval[0]=-1;
 83     int Tlen=strlen(T);
 84     while(i<Tlen)
 85     {
 86         if(j==-1||T[i]==T[j])
 87         {
 88             i++;
 89             j++;
 90             if(T[i]!=T[j]) nxtval[i]=j;
 91             else  nxtval[i]=nxtval[j];
 92         }
 93         else j=nxtval[j];
 94     }
 95 }
 96 
 97 
 98 int main()
 99 {
100     int T;
101     scanf("%d",&T);
102     int cas=0;
103     while(T--)
104     {
105         scanf("%s",s);
106         strcpy(t,s);
107         strcat(s,t);
108         int a,b,c;
109         a=b=c=0;
110         int ls=strlen(s);
111         int lt=strlen(t);
112         EKMP(t,lt,s,ls,nxt,extend);
113         kmp_pre(t,lt,nxtval);
114         int p=lt-nxtval[lt];
115         int tmp=1;
116         if(lt%p==0) tmp=lt/p;
117         for(int i=0;i<lt;i++)
118         {
119             if(extend[i]==lt) a++;
120             else if(s[i+extend[i]]>t[extend[i]]) c++;
121             else b++;
122         }
123         printf("Case %d: %d %d %d\n",++cas,b/tmp,a/tmp,c/tmp);
124     }
125     return 0;
126 }
扩展kmp求最小循环节方法一:kmp预处理
技术分享扩展kmp计算最小循环节方法二:利用已知的next数组

【知识点】

扩展kmp的next数组与kmp数组的next含义不同,是字符串s的所有后缀和s本身的最长公共前缀

【坑点】

做这道题踩了各种坑

  • strcat函数的用法:strcat(s,s)是错误的,会T,strcat的两个参数传的是指针,就是s在内存里面的位置,这里两个s是同一个东西 第一个s变长的时候,第二个s也会变长,然后就没完没了了
  • kmp求最小循环节这里
  • void kmp_pre(char x[],int m,int nxtval[])
    {
        int i,j;
        j=nxtval[0]=-1;
        i=0;
        while(i<m)
        {
            while(-1!=j && x[i]!=x[j]) j=nxtval[j];
            nxtval[++i]=nxtval[++j];
        }
    }

    会WA

  • 正确的是这个
  • void kmp_pre(char x[],int m,int nxtval[])
    {
        int i,j;
        j=nxtval[0]=-1;
        i=0;
        while(i<m)
        {
            if(j==-1||x[i]==x[j])
            {
                i++;
                j++;
                if(x[i]!=x[j]) nxtval[i]=j;
                else  nxtval[i]=nxtval[j];
            }
            else j=nxtval[j];
        }
    }

以上是关于扩展kmp+最小循环节HDU 4333 Revolving Digits的主要内容,如果未能解决你的问题,请参考以下文章

hdu 4333 扩展kmp+kmp重复字串去重

hdu4333 Revolving Digits 扩展kmp

扩展KMP,附上例题(HDU - 4333 Revolving Digits)

HDU 4333 Revolving Digits [扩展KMP]学习笔记

HDU 3746 - Cyclic Nacklace - [KMP求最小循环节]

hdu 3746 Cyclic Nacklace (KMP求最小循环节)