字符串匹配——KMP算法 & BM算法(Knuth-Morris-Pratt & Boyer-Moore)

1-目的:在字符串A中找出字符串B

普通思路:对比两者第一个字符,相同则比较下一位,直到发现不匹配或者比较完整个字符串。若发现不匹配,则回到第一位然后将A的下一位与B的第一位比较,但是这样效率太低

2-KMP算法:

思路:当发现不匹配时,不是向后移动一位比较,而是向后移动(已匹配字符数-部分匹配值)位进行比较

(但是当第一位就不匹配时还是移动一位比较)

2.1-部分匹配值的计算

部分匹配值=当前已匹配部分的“前缀”和“后缀”的最长共有元素的长度
eg. cyzcxccy

字符串 前缀 后缀 最长共有元素的长度
c 0
cy c y 0
cyz c,cy z,yz 0
cyzc c,cy,cyz c,zc,yzc 1
cyzcxccy c…cy… y…cy… 2

2.2-实现

#include 
char a[20]="wacykjjdkwacykoluqbn",b[5]="acyko";
int pmv[5]={-1,-1,-1,-1,-1};partial matching value部分匹配值
int calculate(int n){
    if(pmv[n]!=-1){
        return pmv[n];当已经求过这个位置的部分匹配值,则直接返回
    }
    else{没求过这个位置的部分匹配值,则自己求
        int j,k;
        pmv[n]=0;
        for(j=n-1;j>=1;j--){
            for(k=1;k<=j;k++){
                if(b[k]!=b[n-j-1+k]){
                    break;
                }
                if(k==j){
                    pmv[n]=j;
                    return j;
                }
            }
        }
        return 0; 
    }
}
int main(){
    int i,start,num=0,len1=20,len2=5;
    for(start=0;start<=len1-len2;start++){
        for(i=0;i<=4;i++){   
            if(a[start+i]!=b[i]){
                if(i!=0){
                    start=start+i-calculate(i)-1;
                    此处-1是因为循环语句还会进行start++;
                }
                break;
            }
            else if(i==len2-1){
                printf("the position is %d\n",start);
                return 0;
            }
        }
    }
    return 0;
}
<< the position is 10

3-BM算法:

思路:(右对齐比较字符串)
  1. 从b的串尾开始匹配:
    eg. abxdefgabc 直接比较第三个 xc
    字符串匹配——KMP算法 & BM算法(Knuth-Morris-Pratt & Boyer-Moore)_第1张图片

  2. 得知x与c不匹配之后,同时发现x不存在于字符串b
    所以直接将字符串移动到x后面
    字符串匹配——KMP算法 & BM算法(Knuth-Morris-Pratt & Boyer-Moore)_第2张图片

  3. 若字符串a中不匹配的那个字符存在于字符串b的别处,
    则b串中处在最右的那个“别处”与a串尾的那个字符对齐
    字符串匹配——KMP算法 & BM算法(Knuth-Morris-Pratt & Boyer-Moore)_第3张图片

  4. 匹配到了中间遇到不匹配的字符:
    eg. abcdefg 和 xyefg 到了 d 和 y不匹配
    此时我们要在x~y寻找有没有和efg或efg的子串匹配的串
    若有的话,那它必然也和a串中的efg的子串匹配,然后让他们对齐
    字符串匹配——KMP算法 & BM算法(Knuth-Morris-Pratt & Boyer-Moore)_第4张图片

  5. 这些移动的规则,可以在每次不匹配的时候都算一下,
    然后选择移动较多的数字来移动

3.1-实现(存疑)

#include 
char a[16]="abcdexabcdfgyefg",b[6]="fgyefg";
int len1=16,len2=6,start,i,j,k,walk=0;
int pan(char c){
如果有相同的字符,就让他们对齐,如果没有就把b串后移len2-i长度
    for(j=len2-2-i;j>=0;j--){
        if(c==b[j]){
            walk=len2-j-i;
            if(walk<0){
                walk=-1*walk;
            }
            return walk;
        }
    }
    return walk=len2-i;
}
int main(){
    for(start=len2-1;start<len1;){
        for(i=0;i<=len2-1;i++){
            if(a[start-i]==b[len2-1-i]){
                if(i==len2-1){
                   printf("the position is %d\n",start-len2+1);
                   return 0;
                }
            }
            else{
                start+=pan(a[start-i]);
                break;
            }
        }
    }
}
<< the position is 10

你可能感兴趣的:(算法)