那些有关求解next数组的算法

next数组的历史

  有关字符串的模式匹配算法中,比较容易写出的是朴素的匹配算法也就是一种暴力求解方式,但是由于其时间复杂度为子串长度和主串长度的乘积,例如strlen(subStr) = n,strlen(mainStr) = m,则其时间复杂度为O(mn)。

  为了能够得到更有效的匹配算法,D.E.Knuth与V.R.Pratt和J.H.Morris同时发现,因此人们称它为克努特--莫里斯--普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。这也即是KMP算法的设计思想。

next数组的求解思路

  求next数组,next[j]表示,当模式串j位置与主串i位置处发生不匹配时,i指针不回溯,j指针回溯到next[j]的位置。

  对于求next[j]有三种情况:

1、j = 0时,next[j] = -1;//即模式串的第一个字符与主串i位置发生不匹配,应将i跳过当前位置,从下一个位置和模式串的第一个字符继续比较。

2、假设已知next[j] = k,即subStr[0,...,k-1] = subStr[j-k,j-1]。当subStr[k] = subStr[j]时,也就是说模式串满足subStr[0,...,k] = subStr[j-k,j],可以得知next[j+1] = k + 1 = next[j] + 1;

3、当subStr[k] != subStr[j]时,就需要从k位置之前去查找与subStr[j]匹配的位置,假设为j'。这样问题又可以转化为第二种情况,即next[j+1] = next[j'] + 1 = k' + 1。

三种求解next数组的算法

  但是如何去求解next数组呢?有关这个问题,我思考了很长时间,下面给出几种算法:

  算法一,严格根据next数组的定义:

 

 1 void getNext(char subStr[],int next[])
 2 {
 3     int i = 1, j = i - 1,k = -1;
 4     next[0] = -1;
 5     while (i < strlen(subStr))
 6     {
 7         //k = -1时表示j指针回溯到第一个字符的位置
 8         //subStr[k] == subStr[i-1]表示第k个字符和i - 1个字符相等,属于情况二
 9         if (k == -1 || subStr[k] == subStr[i-1])
10         {
11             next[i] = k + 1;
12             k = next[i];
13             i++;
14         }
15         //情况三,不相等的话,要回溯j指针,subStr[j'] = subStr[i-1]的位置j'
16         else
17         {
18             int t = i - 2;
19             while (t>=0)
20             {
21                 if (subStr[t] == subStr[i - 1])
22                 {
23                     j = t;
24                     break;
25                 }
26                 t--;
27             }
28             if (t < 0)
29                 j = 0;
30             k = next[j];
31         }
32 
33     }
34 }

  算法二,算法的设计思想和算法一大致相同

void getNext(const char P[], int next[])
{
    int q, k;
    int m = strlen(P);
    next[0] = -1;//模版字符串的第一个字符的最大前后缀长度为0
    for (q = 1; q < m; ++q)//for循环,从第二个字符开始,依次计算每一个字符对应的next值
    {
        k = next[q - 1];
        while (k > 0 && P[q - 1] != P[k])//迭代地求出P[0]···P[q]的最大的相同的前后缀长度k
        {
            k--;
        }

        if (P[q-1] == P[k])//如果相等,那么最大相同前后缀长度加1
        {
            k++;
        }
        if (k == -1)
            k = 0;
        next[q] = k;

    }
}

  算法三,更加优化的求解next数组的算法

void getNext(const char subStr[], int next[])
{
    int i = 1, j = -1;
    next[0] = -1;
    while (i < strlen(subStr))
    {
                //i从0开始的,属于情况二
                //j是前后缀长度
        if (j == -1 || subStr[i] == subStr[j])
        {
            i++;
            j++;
            next[i] = j;
        }
                //情况三,不同则j指针回溯
        else
            j = next[j];
    }
}

  现在来进行总结一下,对于算法一和算法二来说,它们的时间复杂度是一样的,但是相对于算法三来说,虽然不如算法三高效,但是比较容易理解!

PS:如果有误的地方,请指出,共同进步!

posted on 2015-11-26 11:07  RunningSnail 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/tgycoder/p/4997067.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,其中的关键是求解next数组。在求解next数组的过程中,我们需要使用到前缀和后缀的概念。具体算法如下: 1. 首先,我们初始化一个next数组,长度为待匹配字符串的长度n。将next置为-1。 2. 接下来,我们使用两个指针i和j来遍历待匹配字符串。其中i表示当前待匹配字符串的位置,j表示子串的长度。 3. 在循环中,我们首先判断pStr[k]是否等于pStr[j],如果相等,说明找到了一个最大前后缀的匹配部分。此时,我们将next[j+1]赋值为k+1,并将k的值增加1。 4. 如果pStr[k]不等于pStr[j],说明当前子串不匹配。我们需要将子串的长度减一并重新计算next数组的值。在这里,我们将j的值设置为j-k,并检查k的值是否为0。 5. 如果k不为0,说明我们还可以继续优化。我们将k的值更新为next[k],这样可以跳过一些不必要的比较。 6. 通过以上步骤,我们可以得完整的next数组。最终,将next数组返回。 这就是KMP算法求解next数组的过程。通过使用next数组,我们可以在字符串匹配过程中更高效地移动指针,从而提高算法的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [KMP算法之next数组详解](https://blog.csdn.net/qq_43450920/article/details/110532224)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [KMP算法中的next数组求解](https://blog.csdn.net/guishangppy/article/details/126668030)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值