• KMP算法心得


    今天又看了一遍KMP,感觉真的懂了...就来这儿发一下心得吧.

    KMP算法其实就是暴力的改进版.让我们看看暴力的匹配.

    Original string: ababababcbbababababc
    Pattern string:  abababc

    步骤:

    ababababcbbababababc
    abababc
    ....中间一些步骤
    ababababcbbababababc
    abababc
    这里a和c匹配不了了,传统的作法会从第二个字符`b'开始匹配.明显不行又跳出.即:
    ababababcbbababababc
    a...
    再从第三个字符`a'开始:
    ababababc...
    abababc
    现在匹配了.继续重复.

    很明显这个算法在极端情况下的时间复杂度是$ ext{O}left( lenleft( ext{Orig String} ight) cdot lenleft( ext{Patt String} ight) ight)$.效率很低.

    想一想.在Brute Force中,每次失配后都会将Pattern的头指针指向下一个字符匹配,相当于每次失配都只能跳过一个字符.显然这么做效率非常低.能不能一次跳过多个字符却依然不会漏过匹配呢?当然可以.这样跳过的要求是什么呢?

    看看这个例子:
    ababababc
    abababc
    首先`a'和`c'不匹配.跳到`b'么?明显不行,不匹配.
    注意到模式串中的
    abababc
    最前面的`ab'和无法匹配的`c'前面的`ab'是相同的.那么往前跳两格.为什么不跳四格呢?注意到
    abababc
    中,最前面也有`abab'.
    原因是这两部分长度的和超过了已经匹配的字符串的长度,便也许会漏解.
    那么在这时设置一个`f[i]'数组,表示第i位匹配失败后将i减小到几.
    那么在这个例子中,`f[i]'值如下:(虚拟一个`s[i]'数组表示跳多远)
    patt a b a b a b c
    f[i] 0 0 0 1 2 3 0
    s[i] 0 1 2 2 2 2 6
    i 0 1 2 3 4 5 6
    即s[i]=i-f[i]
    那么如何求这个f数组呢?

    这就是一个有意思的问题了.注意到模式串一般来说要比查找的串要短不少,因此用暴力的$ ext{O}left( n^2 ight)$也算一种减小问题规模的算法.但是这样就不统一了,这个时间复杂度奇怪得很.注意到求这个数组的过程神似字符串匹配,那么我们可以用KMP自己来求解,即考察所有在它前面的字符中能够匹配的最大的串.这个可以用一种类似于动归的办法很方便的求解.

    设已经求到第i位,那么第i+1位就能被方便的求解.(伪代码,p是模式串,下标从0开始)
    f[0]=f[1]=0
    j=f[i]
    while j && p[i]!=p[j]:
    j=f[j]
    f[i+1]=j+1 if p[i]==p[j] or 0

    最后附上代码

    #include <string.h>
    #include <malloc.h>
    #include <stdio.h>
    void getfail(char* p,int* f){
    	int m=strlen(p),i=1,j;
    	f[0]=f[1]=0;
    	for(;i<m-1;++i){
    		j=f[i];
    		while(j&&p[i]!=p[j]){
    			j=f[j];
    		}
    		f[i+1]=(p[i]==p[j]?++j:0);
    	}
    }
    int match(char* s,char* p,int* res){
    	int l=strlen(s),i,j,lp=strlen(p),lm=0;
    	int* f=(int*)malloc((lp+10)*sizeof(int));
    	if(f==NULL) return -2;
    	getfail(p,f);
    	j=0;
    	for(i=0;i<l;++i){
    		while(j&&p[j]!=s[i]) j=f[j];
    		if(p[j]==s[i]) ++j;
    		if(j==lp){
    			res[lm]=i-lp+1;
    			++lm;
    			j=f[j-1];
    			if(p[j]==s[i]) ++j;
    		}
    	}
    	return lm;
    }
    

    这段代码可以求出所有匹配字符串,并返回匹配数.

  • 相关阅读:
    HDU4366 Successor 线段树+预处理
    POJ2823 Sliding Window 单调队列
    HDU寻找最大值 递推求连续区间
    UVA846 Steps 二分查找
    HDU3415 Max Sum of MaxKsubsequence 单调队列
    HDU时间挑战 树状数组
    UVA10168 Summation of Four Primes 哥德巴赫猜想
    UESTC我要长高 DP优化
    HDUChess 递推
    HDU4362 Dragon Ball DP+优化
  • 原文地址:https://www.cnblogs.com/tmzbot/p/3944749.html
Copyright © 2020-2023  润新知