KMP算法(MP?) HDU 2087 剪花布条 自己卡自己



想法的来源!!!!!!
不懂kmp?请看上面的链接,附上一道模板题 洛谷P3375

题面

这是一道kmp(mp?我也分不清)的模板题(紫书上好像有类似的模板),思路很清楚,下面放出代码

解题

#include 
#include 
using namespace std;

clock_t _timer_start,_timer_end;

void start_timer(){
    _timer_start=clock();
}
void stop_timer(){
    _timer_end=clock();
    double dur=(double)(_timer_end-_timer_start);
    printf("Time Consume:%lf second\n", dur/CLOCKS_PER_SEC);
}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
char src[101010],pattern[101010];
int fail[101010];

void calc_fail(){
    fail[0]=0;
    int len=strlen(pattern+1);
    int k=0;
    for(int i=1;i<=len;i++){
        while(k && pattern[i]!=pattern[k])k=fail[k];
        //下面这一行的有无不影响程序的正确性
        //while(k && pattern[i+1]==pattern[k])k=fail[k];
        fail[i+1]= pattern[i]==pattern[k]?++k:0;
    }
}
long long jump=0;
long long ans=0;
void solve(){
    calc_fail();
    int l1,l2;

    l1=strlen(src), l2=strlen(pattern);
    int cnt=0;
    for(int i=0,j=0;i<l1;){
        //jump++;
        if(src[i]==pattern[j]){
            j++;i++;
            if(j==l2){
                cnt++;
                j=0;
            }
        }else{
            if(j!=0)j=fail[j];
            else i++;
        }
    }
    //ans+=cnt;
    printf("%d\n", cnt);
}

int main() {
    //start_timer();
    while(scanf("%s%s", src, pattern)==2 && src[0]!='#')solve();
    //stop_timer();
    //printf("process %lld ans %lld", jump, ans);
}

题目很简单。

开始折腾自己

我在其他博客看到的kmp(mp?)代码大都如上,但是这样的代码存在一个问题:
举个例子

原串(T): aaacaaaa
匹配串(P): aaaa

可以自己模拟一下:
前三个字符都是匹配的
T [ 0 ] = = P [ 0 ] T[0]==P[0] T[0]==P[0]
T [ 1 ] = = P [ 1 ] T[1]==P[1] T[1]==P[1]
T [ 2 ] = = P [ 2 ] T[2]==P[2] T[2]==P[2]
接下来遇到第四个字符,发现 T [ 3 ] ! = P [ 3 ] T[3] != P[3] T[3]!=P[3]
那么我们的算法会怎么处理呢?很明显, f a i l [ 3 ] = = 2 fail[3]==2 fail[3]==2,所以程序会用T[3]与P[2]进行判断。那么问题来了, 我们已经知道了 P [ 3 ] = = P [ 2 ] P[3]==P[2] P[3]==P[2] == ‘a’,那么让程序再跳回位置2显然是浪费的。

既然发现了问题,那。。。

写数据卡自己的代码

#include 
#define ll long long

using namespace std;

#define produce 1
#if produce==1
void redirect_output(char * path){
    freopen(path,"w",stdout);
}

const int kase=10000;
void output();

int ran(int l,int r){
    return double(rand())/RAND_MAX*(r-l)+l;
}

int main(){
    srand(time(NULL));
    redirect_output("/home/lalala/in");
    if(kase==0){
        output();
    }else{
        //printf("%d\n",kase);
        for(int i=0;i<kase;i++) output();
        printf("#\n");
    }
}

void output(){
    int len1,len2;
    len1 = ran(1, 100000);
    len2 = len1/10;
    len2 = ran(1, min(1000, len1));
    for(int i=0;i<len1;i++){
        int r=ran(0,60);i+=r;
        for(int j=0;j<r;j++)printf("%c",'a');
        r=ran(0,4);i+=r;
        for(int j=0;j<r;j++)printf("%c",'c');
    }
    printf("\n");
    for(int i=0;i<len2;i++){
        printf("a");
    }
    printf("\n");
}
#endif

生成了489M的数据 (`・ω・´)
用我们的代码跑一下试试
KMP算法(MP?) HDU 2087 剪花布条 自己卡自己_第1张图片
还不错,用时3.24s

小优化

我们做一点小优化,如下(就是把注释掉的代码恢复 T_T)

 void calc_fail(){
    fail[0]=0;
    int len=strlen(pattern+1);
    int k=0;
    for(int i=1;i<=len;i++){
        while(k && pattern[i]!=pattern[k])k=fail[k];
        //下面这一行的有无不影响程序的正确性
        while(k && pattern[i+1]==pattern[k])k=fail[k];
        fail[i+1]= pattern[i]==pattern[k]?++k:0;
    }
}

那么现在再跑一下呢?
KMP算法(MP?) HDU 2087 剪花布条 自己卡自己_第2张图片
1.98s,快乐呀

总结

当我们发现P[i+1]==P[k]时,可以肯定P[fail[k]]!=P[i+1],于是我们可以继续让k=fail[k]
加一点小优化,遇上毒瘤题也不怕啦~~
Ps. 本垃圾刷的题少T_T,暂时没见过卡这点的题目

更新

貌似这就是kmp与mp的区别
见 https://www.cnblogs.com/NightRaven/p/10554401.html

你可能感兴趣的:(ACM,KMP,MP,HDU,2087,算法,算法)