zoukankan      html  css  js  c++  java
  • 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;
    }
    

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

  • 相关阅读:
    solidworks 学习 (二)洗手液瓶
    solidworks 学习 (一)螺丝刀
    tensorflow 2.0 学习(三)MNIST训练
    tensorflow 2.0 学习(二)线性回归问题
    tensorflow 2.0 学习(一)准备
    sscanf linux-c从一个字符串中读进与指定格式相符的数据
    Linux-c glib库hash表GHashTable介绍
    Linux-c给线程取名字
    linux-c getopt()参数处理函数
    golang Linux下编译环境搭建
  • 原文地址:https://www.cnblogs.com/tmzbot/p/3944749.html
Copyright © 2011-2022 走看看