HDU 4333:Revolving Digits KMP+扩展KMP

来源:互联网 发布:天猫淘宝商城童装女 编辑:程序博客网 时间:2024/06/10 03:07

扩展KMP的姿势
扩展KMP可以在O(n)的复杂度内求这样一个问题:
给定两个串S,T,设n=|S|,m=|T|S中的每个后缀与T的最长公共前缀,用extend[i]表示。即extend[i]=LCP(S[i..n],T)
算法过程:
类似的,我们令next[i]表示T中的每个后缀与T的最长公共前缀,即next[i]=LCP(T[i..m],T)
k表示当前k+next[k]1最大的k,并令p=k+next[k]1
假设当前要计算extend[i],易知S[k..p]=T[1..pk+1]
则有S[i..p]=T[ik+1,pk+1]
L=next[ik+1],
L<pi+1,则extend[i]=L
否则,先令extend[i]=pi+1(注意pi+1不能<0,如果<0就赋为0),然后从S[i+next[i]]T[next[i]+1]向后匹配,直到失配为止。
算法结束后,如果当前i+next[i]>k+next[k],则更新k
求解next数组的过程与本过程类似,其实就是T自身的扩展KMP过程。
扩展KMP算法是十分优秀的,常见的应用有:
1.求最长公共前缀。
2.求解重复子串的长度:
如串abababc,next[3]=4,next[5]=2,重复子串ababab的长度即i+next[i]1=6,再如串ababa,next[3]=3,next[5]=1,重复子串ababa的长度即i+next[i]1=5。所以重复子串的长度=端点处的i+next[i]1,若最后一个循环节不完整也将其算在内。

然后这道题,我们可以把原串补在后面一遍,然后求出next数组,若next[i]>=len,证明两个串相等,否则我们只需要比较s[next[i]+1]s[i+next[i]]的大小,就能得出两个串的大小关系。
然后至于重复的串,我们可以用kmp来求一下最小循环节,将三个答案都除以最小循环节出现的个数就好了。

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int a,b,c,len;int p[100005],q[100005];char s[200005];int main(){    int testcase;    scanf("%d",&testcase);    for (int T=1;T<=testcase;T++)    {        scanf("%s",s+1);        int j=0; p[1]=0;        len=strlen(s+1);        for (int i=2;i<=len;i++)        {            while (j&&s[j+1]!=s[i]) j=p[j];            if (s[j+1]==s[i]) j++;            p[i]=j;        }        int tmp=len%(len-p[len])==0?len/(len-p[len]):1;        for (int i=1;i<=len;i++) s[i+len]=s[i];         len<<=1;        int p=1;        q[1]=len;        while (p<=len&&s[p]==s[p+1]) p++;        q[2]=p-1;         int k=2;        for (int i=3;i<=len>>1;i++)        {            int p=k+q[k]-1;            q[i]=min(q[i-k+1],max(p-i+1,0));            while (i+q[i]<=len&&s[q[i]+1]==s[i+q[i]]) q[i]++;            if (i+q[i]>k+q[k]) k=i;        }        len>>=1;        a=b=c=0;        for (int i=1;i<=len;i++)                if (q[i]>=len) b++;            else if (s[q[i]+1]>s[i+q[i]]) a++;            else c++;        printf("Case %d: %d %d %d\n",T,a/tmp,b/tmp,c/tmp);    }    return 0;}
0 0