月球美容计划之那些天我们学过的KMP

问题描述

给两个字符串A,B,如A=aaaaaaaaaaaaaaaaaaab,

B = aaaaaaaab”,现在要求B是否是A的字串。

 

普通方法(O(nm)),对两个字符串进行双重枚举进行匹配知道找到A字符串中的B字符串。

 

for(i = 0;i < n;i++)

{

int tf = 0;

for (k = 0;k < m;k++)

{

if (A[i] != B[k])

{

tf = 0;

break;

}

}

if (tf)

break;

}


 

 

KMP小忆

KMP算法是通过分析模式字符串,预先计算每个位置发生不匹配的时候,所需跳转到的下一个比较位置,整理出来一个next数组,然后在上面的算法中使用。

其达到的最终效果是,避免记录字符串A的指针i回溯,从而节省一个循环,也就是把Omn),降为Om)。

因此,也就可以说,KMP就是对Onm)算法的改良,因此,其改良的地方,就是其关键。也就是说next就是KMP的精华所在。

犹抱琵琶的next函数

寒假时总结的模板

void kmp (char *T,int next[])

{

    int j = 0,k = -1;

    next[0] = -1;

 

    while (T[j] != '\0')

    {

        if (k == -1 || T[j] == T[k])

        {

            j++;

            k++;

            next[j] = k;

        }else

            k = next[k];   //相当于在求next的时候利用已求出的next

    }

}


 

 

这个是非改进版的求next的函数。其next数组记录的数据x代表这个元素的位置,代表与整个字符串T的前x个字符相同。

不能小看next

next的使用不仅仅是其储存的内容的或用上,甚至不能小瞧在求next数组的时候其产的中间值。

记得有一个题,就是让求字符串中循环节的长度,记得以前做类似的题目的时候是用的双重枚举,如果用KMP的话,也是可以省掉的一层循环。

做这题求next的代码如下:

 


int fnext (char *T,int next[])

{

    int j = 0,k = -1;

    next[0] = -1;

 

    while (T[j] != '\0')

    {

        if (k == -1 || T[j] == T[k])

        {

            j++;

            k++;

            next[j] = k;

        }

        else

            k = next[k];

    }

 

    if (k == 0)

        return -1;

    return j - k;

}


 

其中j指针指的是主字符串(不回溯的指针),k字符串指的是辅字符串(可以回溯),当next数组构建完成的一瞬间,想象一下jk的样子,现在j指向了主字符串的最末尾,而k不一定指向哪,但可以肯定的是,如果T[j] != T[k]k一定不等于0,否则,k指向的位置x,一定会让字符串后x位与前x位相等。

也就是说,如果存在循环节,其循环节的长度就是j - k

 

 

KMP带来的反思

KMP使用一个next数组让一种O(nm)的算法变为了O(n),有点用空间换时间的味道,像这种用空间换时间还有什么可以举一反三的吗?

KMP是利用空间,避免了在双重枚举的时候,一个指针的反复回溯,减少了一层循环。

那么,在做模拟题,尤其涉及到枚举的模拟题的时候,是不是在时间吃紧的情况下可以考虑通过存储某些东西,来去掉一层循环呢?

 

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页