KMP算法中next数组的计算

GetNext的个人理解:

首先先说一下next串的定义吧:


在对于字符串t中每个字符t[j](0<=j<=len-1),存在一个整数k(k<j),使得模式串t

开头的k个字符依次与t[j]的前面的k个字符相同。

#include"顺序串.cpp"//关于顺序串的基本操作,下边放有源代码
void GetNext(SqString t,int next[])//求字符串t的next数组
{
    int j,k;
    j=0;
    k=-1;//j扫描t,k记录t[j]之前于t开头相同的字符个数
    next[0]=-1;
    while(j<t.len)//求t所有位置next的值
    {
        if(k==-1||t.data[j]==t.data[k])
        {
            j++;k++;
            next[j]=k;
        }
        else
            k=next[k];//回退
    }
}
int KMPIndex(SqString s,SqString t)//返回值为t的第一个字符在s中出现的下标
{
    int next[20],i=0,j=0;
    GetNext(t,next);
    while(i<s.len&&j<t.len)
    {
        if(j==-1||s.data[i]==t.data[j])
        {
            i++;
            j++;
        }
        else
            j=next[j];
    }
    if(j>=t.len)
        return (i-t.len);
    else
        return (-1);
}
int main()
{
    char a1[]="aaaaaaxyzaaaa",a2[]="ababc";
    SqString s1,s2;
    StrAssign(s1,a1);
    StrAssign(s2,a2);
    //cout<<BF(s1,s2);
    cout<<KMPIndex(s1,s2);
    return 0;
}

关于顺序串的基本操作。

#include<bits/stdc++.h>
#include<cstdio>
#include<stdlib.h>
#define MaxSize 100
using namespace std;
typedef struct
{
    char data[MaxSize];
    int len;
} SqString;
void StrAssign(SqString &str,char cstr[])//将一个字符串常量赋给串str,即生成一个其值等于cstr的串s。
{
    int i;
    for (i=0; cstr[i]!='\0'; i++)
        str.data[i]=cstr[i];
    str.len=i;
}
void StrCopy(SqString &s,SqString t)  //将串t复制给串s
{
    int i;
    for (i=0; i<t.len; i++)
        s.data[i]=t.data[i];
    s.len=t.len;
}
int StrEqual(SqString s,SqString t)//判断两个串是否相等:若两个串s与t相等返回真(1);否则返回假(0)
{
    int same=1,i;
    if (s.len!=t.len) same=0; //长度不相等时返回0
    else
        for (i=0; i<s.len; i++)
            if (s.data[i]!=t.data[i]) /*有一个对应字符不同时返回0*/
            {
                same=0;
                break;
            }
    return same;
}
int StrLength(SqString s)//求串长:返回串s中字符个数
{
    return s.len;
}
SqString Concat(SqString s,SqString t)//返回由两个串s和t连接在一起形成的新串
{
    SqString str;
    int i;
    str.len=s.len+t.len;
    for (i=0; i<s.len; i++) /*s.data[0..s.len-1]=>str*/
        str.data[i]=s.data[i];
    for (i=0; i<t.len; i++) /*t.data[0..t.len-1]=>str*/
        str.data[s.len+i]=t.data[i];
    return str;
}
SqString SubStr(SqString s,int i,int j)//返回串s中从第i(1≤i≤StrLength(s))个字符开始的、由连续j个字符组成的子串。
{
    SqString str;
    int k;
    str.len=0;
    if (i<=0 || i>s.len || j<0 || i+j-1>s.len)
    {
        printf("参数不正确\n");
        return str;    /*参数不正确时返回空串*/
    }
    for (k=i-1; k<i+j-1; k++)   /*s.data[i..i+j]=>str*/
        str.data[k-i+1]=s.data[k];
    str.len=j;
    return str;
}

SqString InsStr(SqString s1,int i,SqString s2)//将串s2插入到串s1的第i个字符中,即将s2的第一个字符作为s1的第i个字符,并返回产生的新串
{
    int j;
    SqString str;
    str.len=0;
    if (i<=0 || i>s1.len+1)  /*参数不正确时返回空串*/
    {
        printf("参数不正确\n");
        return s1;
    }
    for (j=0; j<i-1; j++) /*s1.data[0..i-2]=>str*/
        str.data[j]=s1.data[j];
    for(j=0; j<s2.len; j++) /*s2.data[0..s2.len-1]=>str*/
        str.data[i+j-1]=s2.data[j];
    for (j=i-1; j<s1.len; j++) /*s1.data[i-1..s1.len-1]=>str*/
        str.data[s2.len+j]=s1.data[j];
    str.len=s1.len+s2.len;
    return str;
}
SqString DelStr(SqString s,int i,int j)//从串s中删去第i(1≤i≤StrLength(s))个字符开始的长度为j的子串,并返回产生的新串。
{
    int k;
    SqString str;
    str.len=0;
    if (i<=0 || i>s.len || i+j>s.len+1)
    {
        printf("参数不正确\n");
        return str;   /*参数不正确时返回空串*/
    }
    for (k=0; k<i-1; k++)  /*s.data[0..i-2]=>str*/
        str.data[k]=s.data[k];
    for(k=i+j-1; k<s.len; k++)
        /*s.data[i+j-1..s.len-1]=>str*/
        str.data[k-j]=s.data[k];
    str.len=s.len-j;
    return str;
}
SqString RepStr(SqString s,int i,int j,SqString t)//在串s中,将第i(1≤i≤StrLength(s))个字符开始的j个字符构成的子串用串t替换,并返回产生的新串
{
    int k;
    SqString str;
    str.len=0;
    if (i<=0 || i>s.len || i+j-1>s.len)
        /*参数不正确时返回空串*/
    {
        printf("参数不正确\n");
        return str;
    }
    for (k=0; k<i-1; k++)	     /*s.data[0.i-2]=>str*/
        str.data[k]=s.data[k];
    for (k=0; k<t.len; k++)    /*t.data[0..t.len-1]=>str*/
        str.data[i+k-1]=t.data[k];
    for(k=i+j-1; k<s.len; k++) /*s.data[i+j-1..s.len-1]=>str*/
        str.data[t.len+k-j]=s.data[k];
    str.len=s.len-j+t.len;
    return str;
}
void DispStr(SqString s)//输出串s的所有元素值
{
    int i;
    if (s.len>0)
    {
        for (i=0; i<s.len; i++)
            printf("%c",s.data[i]);
        printf("\n");
    }
}

KMP算法的优化nextval[]:

void GetNextval(SqString t,int nextval[])
{
    int j=0,k=-1;
    nextval[0]=-1;
    while(j<t.len)
    {
        if(k==-1||t.data[j]==t.data[k])
        {
            j++;
            k++;
            if(t.data[j]!=t.data[k])
                nextval[j]=k;
            else
                nextval[j]=nextval[k];
        }
        else
            k=nextval[k];
    }
}

书上代码的思路:

当找第j个字符时,考虑t[j]是否与t[next[j]]相等,(next数组中存储的是相同的字符的个数,同时这个数在数值上是与{相等字符个数后一个字符的下标}所相等的)

每一个tnext值都是与前一位的存储相关联,所以要与前一位的t[k]的字符进行比较。

if相等:

j++,k++;

next[j]=k;

j++(因为next中位置的数据信息存储在j+1的位置上)

k++(因为比较字符相同,所以只需要在前者的k+1即可)

else不相等:

K回退

找到0~k个字符“大串中”,找到已经匹配好的“小串”,若找到与t[j]相等的字符,

则把“当前”的k+1存入。若找不到,则此时的k一定回溯到了k=(-1);k++j++

此时t[j]nextnext[j+1]=-1+1=0;正好为0,没有相同的字符串。

由此也就解释了为何要将next[0]=-1。(可能有疑惑为什么小串的数值一定是匹配好的。::因为小串是在大串中的,而我们找的大串就是匹配好的大串,所以小串数值一定也是匹配好的)

    (emmmm:当找不到相同的字符时,k=-1k++,j++k=0,正好符合0个字符与开头字符相同。存储在next[j+1]中,即next[j+1]=0j的数据信息)

    nextval优化的特点就是将失配的字符于此时next[]的值下的字符进行比较,若想等则直接递推,即nextval[j]=nextval[k];因为这两个字符串是相等的再与主串匹配时同样会失配。(当不相等时,则与next的赋值一样,另nextval[j]=k);例如A=C,C=C1,所以A=C1.所以相等时可直接跳过一次不必要的回溯,此优化是在next中进行的优化,优化结果为新的数组nextval[].

    在这里我提供一种方便记忆的方法:1) t[j]!=t[k],nextval[j]=next[j];

                    2) t[j]==t[k],nextval[j]=nextval[k];

                    即:不相等,不变。相等,继承。

 时间关系没有细写,我会再次完善本篇文章。奋斗

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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、付费专栏及课程。

余额充值