KMP算法求回溯数组的步骤

KMP算法到底是什么原理就不说了,各种资料上讲的明明白白,下面我就如何用代码来实现做一下说明和记录.

KMP的核心思想就是,主串不回溯,只模式串回溯。而模式串匹配到第几位时失配,要回溯多少,由模式串本身来决定。

而求回溯数组主要思想是:

>模式串同时看做新的主串与模式串,自己匹配自己。

>每次匹配都能产生一个数组值,而这个数组值,就是模式串当前偏移量。

>如果模式串失配位置不是首位,那么,还要确定主串失配位置和模式串的首位是否匹配,因为上次匹配的时候,该位置的数组值已经赋了。

所以这次匹配不需要修改数组值。

>如果模式串失配位置是首位,那么,不需上述判断,直接主串后移一位。

代码流程如下:

1. 把模式串p分别作为主串pm和模式串pp进行匹配。

2. index[0] = 0;

3. 主串从pm[1] -> pm[strlen(p) - 1]循环,每次循环确定一个index[i]。

4. 一个内循环,用模式串(从pp[0]开始)的每一位去匹配主串(主串不回溯,所以它的位置由上次循环来决定)的每一位。

5. index[i] = 当前模式串的下标。

6. 每次循环中,如果匹配,内循环继续比较pm和pp的下一位;如果不匹配(失配),则分2种情况。

7. 情况1,失配时模式串pp的下标不为0,但是我们不确定失配时主串pm失配位置所在的字符是否与匹配串pp的首字符相同,所以,下次匹配要从失配位置开始。

但是,注意,因为在第5步我们在失配位置的index[i]已经赋值了,所以,有可能重复赋值。这一点可以做一些必要判断(如:index[i]是否首次赋值;本次循环是否是

为上次失配而进行的收尾工作)。

8. 情况2,失配时模式串pp的下标为0,所以我们不必担心7中的问题,主串下标直接移到下一位置即可。

由此,整个步骤就完成了。

代码如下:

static int *kmp(int *index, int length, char *pm){
    for(int i = 0; i < length; i++){
        index[i] = -200;
    }
    //1. pm是主串,pp是模式串
    char *pp = pm;
    //2. index[0] = 0
    index[0] = 0;
    //3. 因为index[0]已确定,因此主串pm从pm + 1处开始循环。
    pm += 1;
    while(*pm != '\0'){
        int i = -1;
        //4. 内循环,模式串pp从0开始按位匹配pm的每一位。
        for(i = 0; i < length; i++){
            //5. index[pm - pp + i] = i
            if(index[pm - pp + i] == -200)//index[i]只允许赋值一次。为了防止第7步中【注意】中所说的情况
                index[pm - pp + i] = i;
            //6. 如果匹配,则继续循环,否则就是失配,分2种情况。
            if(pm[i] != pp[i]){
                //7.失配时模式串位置不为0,这时候pm移动至当前失配的位置。
                if(i != 0){
                    pm += i;
                }
                //8. 失配时模式串位置为0,这时候pm移动到下一位置。
                else pm++;
                break;
            }
        }
    }
    return index;
}

由此,回溯数组确定了。那么kmp要如何使用这个数组呢?

>匹配主串与模式串,如果失配,主串和匹配串都要改变位置,分2种情况

>如果模式串失配位置是0,主串移动到失配的下一位置。如果失配位置不是0,则主串移动到失配位置。

>模式串移动到回溯数组中的指定位置。

代码如下:

int find(char *s, char *p){
        char *src = s;//主串
        char *psrc = p;//模式串
        int pm_len = strlen(pm);
        int *index = new int[pm_len];
        kmp(index, pm_len, p);//回溯数组 
        while(*s != 0){ 
            bool suc = true; 
            int i = -1; 
            for(i = 0; i < strlen(p); i++){
                if(p[i] != s[i]){//失配, i表示模式串失配位置 
                    //如果失配位置不为0,则移动主串到失配位置。 
                    if(i != 0) s += i;//如果失配位置为0,则移动主串到失配位置的下一位。 
                    else s++; //只要失配,模式串都移动到回溯数组中的指定位置。 
                    p = psrc + index[i]; 
                    suc = false; 
                    break; 
                    } 
                }
        }
    //每次模式串匹配完之后,判断是否成功了。 
        if(suc){
            delete []index;
            return s - src; 
            } 
        }
        delete []index;
        return -1;
     }


KMP算法是一种用于字符串匹配的高效算法,其中的next数组是该算法的核心部分之一。next数组用于记录模式串中每个位置的最长公共前缀和最长公共后缀的长度。 具体来说,next数组的定义如下: 1. next = -1,表示模式串的第一个字符没有前缀和后缀。 2. 对于模式串中的每个位置i(1 <= i < 模式串长度),next[i]表示模式串前缀子串[0, i-1]中最长的既是前缀又是后缀的子串的长度。 通过构建next数组,可以在匹配过程中根据已匹配的前缀信息来决定下一步的移动位置,从而避免不必要的比较。 下面是构建next数组步骤: 1. 初始化next = -1,j = 0,i = 1。 2. 当i < 模式串长度,执行以下步骤: - 如果模式串的第i个字符与模式串的第j个字符相等,则令next[i] = j,i++,j++。 - 如果模式串的第i个字符与模式串的第j个字符不相等: - 如果j = 0,则令next[i] = 0,i++。 - 如果j != 0,则令j = next[j],回溯到上一个最长公共前缀和最长公共后缀的长度,继续比较。 构建完next数组后,可以根据next数组来进行字符串匹配,具体步骤如下: 1. 初始化文本串的指针i = 0,模式串的指针j = 0。 2. 当i < 文本串长度,执行以下步骤: - 如果文本串的第i个字符与模式串的第j个字符相等,则i++,j++。 - 如果j = 模式串长度,则表示匹配成功,返回匹配位置。 - 如果文本串的第i个字符与模式串的第j个字符不相等: - 如果j = 0,则i++。 - 如果j != 0,则令j = next[j],回溯到上一个最长公共前缀和最长公共后缀的长度,继续比较。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值