码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 4333 Revolving Digits 扩展KMP

时间:2014-07-04 08:42:50      阅读:355      评论:0      收藏:0      [点我收藏+]

标签:扩展kmp

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

题意:给以数字字符串,移动最后若干位到最前边,统计得到的数字有多少比原来大,有多少和原来相同,有多少比原来的小。

思路:拓展KMP中的next数组标记的是子串和母串的公共前缀的长度,要将字符串长度变成原来二倍,这样如果变换后不是完全相同的数字也即公共前缀长度大于等于字符串长度,那么字母串公共前缀的下一位的大小比较就是题目所要求的比较。由于相同的数字串只算一次,则只要统计比较第一个“循环节”里的改变数字串即可。

资料:【转】扩展KMP http://wenku.baidu.com/view/8e9ebefb0242a8956bece4b3.html

扩展KMP:
    给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0<=i<lenA),
 
    求出A[i..lenA-1]与B的最长公共前缀长度,记为ex[i](或者说,ex[i]为满足A[i..i+z-1]==B[0..z-1]的最大的z值)。
 
    扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串。

【算法】
    
    设next[i]为满足B[i..i+z-1]==B[0..z-1]的最大的z值(也就是B的自身匹配)。设目前next[0..lenB-1]与ex[0..i-1]均已求出,要用它们来求ex[i]的值。

    设p为目前A串中匹配到的最远位置,k为让其匹配到最远位置的值(或者说,k是在0<=i0<i的所有i0值中,使i0+ex[i0]-1的值最大的一个,p为这个最大值,即k+ex[k]-1),
    
    显然,p之后的所有位都是未知的,也就是目前还无法知道A[p+1..lenA-1]中的任何一位和B的任何一位是否相等。
    
    根据ex的定义可得,A[k..p]==B[0..p-k],因为i>k,所以又有A[i..p]==B[i-k..p-k],设L=next[i-k],则根据next的定义有B[0..L-1]==B[i-k..i-k+L-1]。考虑i-k+L-1与p-k的关系:
    
    (1)i-k+L-1<p-k,即i+L<=p。这时,由A[i..p]==B[i-k..p-k]可以得到A[i..i+L-1]==B[i-k..i-k+L-1],又因为B[0..L-1]==B[i-k..i-k+L-1]所以A[i..i+L-1]==B[0..L-1],这就说明ex[i]>=L。又由于next的定义可得,
 
A[i+L]必然不等于B[L](否则A[i..i+L]==B[0..L],因为i+L<=p,所以A[i..i+L]==B[i-k..i-k+L],这样B[0..L]==B[i-k..i-k+L],故next[i-k]的值应为L+1或更大),这样,可以直接得到ex[i]=L!
    
    (2)i+k-L+1>=p-k,即i+L>p。这时,首先可以知道A[i..p]和B[0..p-i]是相等的(因为A[i..p]==B[i-k..p-k],而i+k-L+1>=p-k,由B[0..L-1]==B[i-k..i-k+L-1]可得B[0..p-i]==B[i-k..p-k],即A[i..p]==B[0..p-i]),然
 
后,对于A[p+1]和B[p-i+1]是否相等,目前是不知道的(因为前面已经说过,p是目前A串中匹配到的最远位置,在p之后无法知道任何一位的匹配信息),因此,要从A[p+1]与B[p-i+1]开始往后继续匹配(设j为目前
 
B的匹配位置的下标,一开始j=p-i+1,每次比较A[i+j]与B[j]是否相等,直到不相等或者越界为止,此时的j值就是ex[i]的值)。在这种情况下,p的值必然会得到延伸,因此更新k和p的值。
    
    边界:ex[0]的值需要预先求出,然后将初始的k设为0,p设为ex[0]-1。
 
对于求next数组,也是“自身匹配”,类似KMP的方法处理即可。唯一的不同点也在边界上:可以直接知道next[0]=lenB,next[1]的值预先求出,然后初始k=1,p=ex[1]。

需要严重注意的是,在上述的情况(2)中,本该从A[p+1]与B[p-i+1]开始匹配,但是,若p+1<i,也就是p-i+1<0(这种情况是有可能发生的,当ex[i-1]=0,且前面的ex值都没有延伸到i及以后的时候)的话,需要将A、B的下标都加1(因为此时p必然等于i-2,如果A、B的下标用两个变量x、y控制的话,x和y都要加1)!!

【时间复杂度分析】

    在KMP和扩展KMP中,不管是A串还是B串,其匹配位置都是单调递增的,故总时间复杂度是线性的,都为O(lenA + lenB)(只是扩展KMP比KMP的常数更大一些)。

【应用】

    KMP和扩展KMP在解决字符串问题中有大用。很多看上去很猥琐的字符串问题,都可以归结到这两种算法之中。另外,这里的“字符串”可以延伸为一切类型的数组,而不仅仅是字符数组。
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int ext[1000005*2];
char ss[1000005*2];
void getnext(char *T,int *next)
{
    int i,length = strlen(T);
    next[0] = length;
    for(i = 0; i<length-1 && T[i]==T[i+1]; i++);
    next[1] = i;
    int a = 1;
    for(int k = 2; k < length; k++)
    {
        int p = a+next[a]-1, L = next[k-a];
        if( (k-1)+L >= p )
        {
            int j = (p-k+1)>0? (p-k+1) : 0;
            while(k+j<length && T[k+j]==T[j]) j++;
            next[k] = j, a = k;
        }
        else next[k] = L;
    }
}
int main()
{
    int tot,a,b,c;
    scanf("%d",&tot);
    for(int ii=1; ii<=tot; ii++)
    {
        a=b=c=0;
        scanf("%s",ss);
        int len=strlen(ss);
        for(int i=len; i<len*2; i++)
            ss[i]=ss[i-len];
        getnext(ss,ext);
        ss[2*len]='\0';
        int k=len;
        for(int i=1;i<=len;i++)
        {
            if(i+ext[i]>=len)
            {
                k=len%i?len:i;
                break;
            }
        }
        for(int i=0; i<k; i++)
        {
            if(ext[i]>=len)
                b++;
            else if(ss[i+ext[i]]>ss[ext[i]])
                a++;
            else c++;
        }
        printf("Case %d: %d %d %d\n",ii,c,b,a);
    }
    return 0;
}



HDU 4333 Revolving Digits 扩展KMP,布布扣,bubuko.com

HDU 4333 Revolving Digits 扩展KMP

标签:扩展kmp

原文地址:http://blog.csdn.net/ooooooooe/article/details/36712115

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!