求解next数组

在学习数据结构的过程中刚刚接触到kmp算法的时候不求甚解。但在写题的时候遇到了苦难,于是课后在百度上看了不少解释,但却感觉有些混乱,如有些下标从零开始,与教科书中有所不同。虽然它们本质上并没有什么区别,但我还是花了一些时间进行理解和消化。

一.next数组的求法

1.根据前面的字符的next值求

下标从1开始也就是教科书上的写法
将next【1】=0;next【2】=1;j=1;
从第 3 个开始,计算第 i 个位置的 next 值时,检查s.a【i】== s.a【j】?
相等,则 next【i】 = next【i-1】 + 1。
不等,则令j= next【i-1】,重复上面的操作直到下标为1还不等,则赋next【i】为1。
下面是具体的代码


void  get_next(int next[],S s)
{
    int i=1,j=0;
    next[1]=0;
    while(i<s.length)
    {
        if(j==0||s.a[j]==s.a[j])
        {
            ++i;
            ++j;
            next[i]=j;
        }
        else
            j=next[j];
    }
}

2.根据最大公共元素长度求

模式串abcaabbabcab
下标01234567891011
next000102012342

如上图所示,下表从零开始,令next【0】=0,前i个字符的最长的相同前后缀即为当前next【i】的值
例如 abcaabbabca的最长的相同前后缀为abca,所以next【10】=4
代码如下:

void get_next(int next[],S s)
{
    next[0]=0;
    for(int i=1,j=0; i<s.length; i++)
    {
        while(j!=0&&s.a[j]!=s.a[i])
            j=next[j-1];
        if(s.a[j]==s.a[i])
            j++;
        next[i]=j;
    }
}

kmp的应用

题: 医学研究者最近发现了某些新病毒,通过对这些病毒的分析,得知他们的DNA序列都是环状的。现在研究者已收集了大量的病毒DNA和人的DNA数据,想快速检测出这些人是否感染了相应的病毒。为了方便研究,研究者将人的DNA和病毒DNA均表示成由一些字母组成的字符串序列,然后检测某种病毒DNA序列是否在患者的DNA序列中出现过,如果出现过,这此人感染了该病毒,否则没有感染。例如,假设病毒的DNA序列为baa,患者1的DNA序列为aaabbba,则感染。患者2的DNA序列为babbba,则未感染。(注意:人的DNA序列是线性的,而病毒的DNA序列是环状的)。

#include<bits/stdc++.h>
#define maxsize 100000
using namespace std;
int next[maxsize];
typedef struct
{
    char a[maxsize];
    int length;
} S;
void bt(S &s)
{
    cin>>s.a;
    s.length=strlen(s.a);
}
void get_next(int next[],S s)
{
    next[0]=0;
    for(int i=1,j=0; i<s.length; i++)
    {
        while(j!=0&&s.a[j]!=s.a[i])
            j=next[j-1];
        if(s.a[j]==s.a[i])
            j++;
        next[i]=j;
    }
}
int kmp(S s1,S s2)
{
    int i=0,j=0;
    while(i<s2.length&&j<s1.length)
    {
        if(j==0&&s2.a[i]!=s1.a[j])
        {
            i++;
            continue;
        }
        if(s2.a[i]==s1.a[j])
        {
            i++;
            j++;
        }
        else
            j=next[j];
    }
    if(j>=s1.length)
        return 1;
    else
        return 0;
}
int main()
{
    do
    {
        int n;
        S s1,s2;
        bt(s1);
        bt(s2);
        if(strcmp(s1.a,"0"))
            ;
        else if(strcmp(s2.a,"0"));
        else
            break;
        char ss[maxsize*2],s[maxsize];
        strcpy(ss,s1.a);
        strcat(ss,s1.a);
        for(n=0; n<s1.length; n++)
        {
            int next[maxsize];
            for(int m=0; m<s1.length; m++)
                s[m]=ss[n+m];
            strcpy(s1.a,s);
            get_next(next,s1);
            if(kmp(s1,s2))
            {
                cout<<"YES"<<endl;
                break;
            }
        }
        if(n==s1.length&&(!kmp(s1,s2)))
            cout<<"NO"<<endl;
    }
    while(1);
}

结果:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值