BF算法和 KMP算法

这是两个关于字符串的查找匹配算法:

字符串:
char*a是链表式字符存储结构,char a[10]是顺序表式存储结构
BF(Brute Force)暴力匹配算法:例,字符串S,T,先匹配S[0],T[0],若想等则向后比较S[1],T[1],
否则比较S[0]T[1],T依次向后移,直到匹配成功后,S的索引再向后移一位(回溯路线)


KMP算法(字符串匹配的算法),由三个人的名字来进行命名的,从最后一个不相同元素的索引上进行匹配
情况一:当字符间没有重复的话,把首地址放到首先不匹配的字符索引上
情况二:如果单个字符有重复相等的,则移动一格进行匹配检验
情况三:如果字符首前缀(首地址相等的字符块)和后缀(匹配的字符前面的字符) 的字符数相等的话,将首地址放到下一块相等的字符索引上
前缀与后缀的字符可以重复,但不能在首字符的索引上相重合
NEXT数组:当模式匹配串T失配时,NEXT数组对应的元素指导应用T串的哪个元素进行下一轮的匹配
KMP算法的关键是求出匹配子串所对应的NEXT数组(原子串对应下面的前缀和后缀相等的数目
遇到相等的情况就退而求全之,前缀不相等时填0(0表示数组中字符的数量),前缀后缀不等时填1,前后缀有一个字符数相等时填2

一个改进KMP算法的例子:

PS:(神TM boy friend算法 与 看毛片算法发火,在网易云上被某鱼鱼忽悠了,他的那个改进算法码的有误,判断下一个字符相同时需要在NEXT数组的下标不为0的情况下。还有,在最开始的字符比较如果不同时,子串会返回-1,需要把子串复原,主串加1)


以下代码:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
//Brute Force
int BF(char a[], char b[]) {
    //字符串变量不能用strlen()求出它里的字符的数量,如果是复制一份数组过来的话,就可以求数量了
    int alen = strlen(a);
    int blen = strlen(b);
    int i = 0, j = 0;
    while (i < alen&&j < blen) {
        if (a[i] == b[j]) {
            i++;
            j++;
        }
        else {
            i = i - j + 1;
            j = 0;
        }
    }
    if (j == blen)
        return i - j + 1;
    else
        return -1;
}
//KMP算法
void getNext(char a[], int *next) {//得到匹配子串的next数组
    int alen = strlen(a);
    int j = -1, i = 0;
    next[i] = j;
    while (i < alen) {
        if (j == -1 || a[j] == a[i]) {
            j++;
            i++;
            if (a[j] == a[i] && j != 0) {
                next[i] = next[j];
                //前缀是数组前面一串字符,后缀是后面与前缀相匹配的一串字符,它们的数量是统一的
                // 在这里如果j!=0&&数组目前的字符与前缀相等时,可以把其视为一个字符块,
                //当其后缀不相同时,与他相同的前缀也没必要进行检验了
            }
            else
                next[i] = j;//当其移动后的字符不同时或j=0时,应该从最开始不相同的那个前缀后的那个下标开始匹配
        }
        else//如果字符不相同时则回溯到上一次前缀不同时的下标进行重新匹配
            j = next[j];
        //next数组是让匹配子串自己搞自己,在整个过程通过j来回溯前缀,其后缀是不断改变的
    }
}
int KMP(char a[], char b[]) {
    int alen = strlen(a);
    int blen = strlen(b);
    int next[100] = { 0 };
    getNext(b, next);
    //给申请的next数组赋值
    int i = 0, j = 0;
    while (i < alen && j < blen) {
        //这里一定要用&&
        if (a[i] == b[j]) {
            i++;
            j++;
        }
        else
            j = next[j];//只回溯匹配子串的下标即可
                        //忽略对主串下标的操作
        if (j == -1) {
            //在开始的字符比较如果不同时,j会返回-1,此时要把主串加1,子串复原
            j=0;
            i++;
        }
    }
    if (j == blen) {
        return i - j + 1;
    }
    else
        return -1;
}
int main() {
    clock_t start, over,over2;
    //char a[] = "hellomy roo roijjk34jkrolleharrolleharolleolldmfejfndmg,a d,fnb,sdnflij4jdhgnijj j997867yroo roolbrotheroller";
    srand(time(0));
    char a[1000020] = { 0 };
    int i;
    for (i = 0; i < 1000000; i++) {
        a[i] = rand() % 220 + 1;//用随机数来给a赋值
    }
    char b[] = "Mikuisletsyitddddd";
    strcat(a, b);//把b拼到a上,并让a的最后一个字符为0
    start = clock();
    int result1 = BF(a, b);
    over = clock();
    int time1 = over - start;
    int result2 = KMP(a, b);
    over2= clock();
    int time2 = over2 - over;
    //数组就是指针,可以直接引用
    if (result1 == -1 || result2 == -1) {
        printf("未查找到子串!\n");
    }
    else {
        printf("BF查找到子串的索引为:%d,用时:%d毫秒\nKMP查找到子串的索引为:%d,用时:%d毫秒\n", result1, time1, result2, time2);
    }
    return 0;
}
用了随机数,做了一下测试,发现效果不明显
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页