对Next数组的认识和KMP模板题

简介: 这几天进行了KMP专题训练,因为之前没有了解过KMP所以过程特别艰难(头发疯狂牺牲),在网上看了几篇文章,做了几道模板题后对KMP有了初步了解,知道了Next数组才是灵魂

对Next数组的认识
Next数组是对于模式串来说的,简单的来说就是一个串,假设有前缀和后缀相等的情况 (字符串的前缀是指字符串的任意首部。比如字符串“abbc”的前缀有“a”,“ab”,“abb”,“abbc”。同样,字符串的任意尾部是字符串的后缀,“abbc”的后缀有“c”,“bc”,“bbc”,“abbc”。)
那么当Next数组不为零的时候就是前一个字符和前缀匹配到了

而Next数组的值就是匹配到字符的长度。
当Next[Next]就是Next进行回溯的时候,意思就是当前位置和前缀匹配到的长度的上一个阶段。还是我们之前的那个字符串,假设我们把末尾加一个a,很显然这时候的最后一个Next的值为4,那么我们现在来看Next4,很清晰的可以看出来是1,这就说明此时有一个后缀和前缀匹配,
不太清楚也可以再加长一些

Next数组寻找的时候就是找到一个位置(假设现在下标来到了9),往前数,找到第一个和前缀第一个字符相同的字符(上图中就是下标为6的位置),此时的位置就是Next数组的第一个阶段(值为4也就是Next[Next[9]]),如果接着继续往前找那么最后的值就是下标在9的时候后缀能和前缀匹配到的最小长度(在上图也即是1)。

第一题(Oulipo )
HDU - 1686
题目大意:给一个主串一个模式串,计算主串中模式串出现了多少次(可重叠)
注意匹配过之后直接指向当前位置

include

include

include

include

include

const int maxn=1e6+10;
using namespace std;
int Next[maxn];
char str[maxn],mo[maxn];
int ans;
void getNext()
{

int i=0;
int j=-1;
int len=strlen(mo);
while(i<len)
{
    if(j==-1||mo[i]==mo[j])
    {
        i++;
        j++;
        Next[i]=j;
    }
    else
        j=Next[j];
}

}
int kmp()
{

int i,j;
int ans=0;
i=j=0;
int l1=strlen(str);
int l2=strlen(mo);
while(i<l1)
{
    while(j!=-1&&str[i]!=mo[j]) j=Next[j];
    i++;j++;
    if(j>=l2)
    {
        ans++;
        j=Next[j];
    }
}
return ans;

}
int main()
{

int t;
scanf("%d",&t);
while(t--)
{
    ans=0;
    scanf("%s%s",mo,str);
    Next[0]=-1;
    getNext();

    printf("%d\n",kmp());
}
return 0;

}

第二题(剪花布条)
HDU - 2087
题目大意:给一个主串一个模式串,计算主串中模式串出现了多少次(不可重叠)
首先计算出模式串的Next数组,然后kmp进行匹配就可以了,注意一点就是在完成一次匹配后应该将模式串指向,上次匹配结束的下一个位置

include<bits/stdc++.h>

using namespace std;

char s[2010],p[2012];
int nex[2021];
int ans;
int getnext(char *p,int nex[])
{

int p_len=strlen(p);
nex[0]=-1;
int k=-1;
int j=0;
while(j<p_len-1)
{
    if(k==-1||p[j]==p[k])
    {
        ++j;
        ++k;
        nex[j]=k;
    }
        
    else
        k=nex[k];
}

}
void kmp(char s,char p)
{

int l=-1;
int s_len=strlen(s),p_len=strlen(p);
getnext(p,nex);
int j=0;
for(int i=0; i<s_len; i++)
{
    while(j&&s[i]!=p[j]) j=nex[j];
    if(s[i]==p[j]) j++;
    if(j==p_len)
    {
        if(i-l>=p_len)
        {
            ans++;
            l=i;
        }
    }
}

}
int main()
{

while(cin>>s)
{
    if(s[0]=='#') break;
    cin>>p;
    getchar();
    ans=0;
    kmp(s,p);
    cout<<ans<<endl;
}

}

第三题( Seek the Name, Seek the Fame )
POJ - 2752
题目大意:给一个字符串,找到所有前后缀的长度
解题思路:通过Next数组的性质,我们知道,Next数组的值就是当前能匹配到前缀的最大长度,回到题目,也就是说,将每个Next数组的值输出,由此得出答案

include

include

include

using namespace std;

const int N=1e6+5;

int Next[N];
char s[N];
char p[N];
void getnext(char *p)
{

Next[0]=-1;
int p_len=strlen(p);
int j=0;
int k=-1;
while(j<p_len)
{
    if(k==-1||p[j]==p[k])
    {
        k++;
        j++;
        Next[j]=k;
    }
    else
    {
        k=Next[k];
    }
}

}
int a[N];
int main()
{

while(scanf("%s",p)!=EOF)
{
    memset(a,0,sizeof(a));
    getnext(p);
    int l=0;
    int ans=strlen(p);
    while(Next[ans]!=0)
    {
        a[l++]=Next[ans];
        ans=Next[ans];    
    }
    for(int i=l-1; i>=0; i--)
        cout<<a[i]<<" ";
    cout<<strlen(p)<<endl;

}

}

相关文章
|
26天前
AcWing 831. KMP字符串
AcWing 831. KMP字符串
8 0
|
算法
KMP算法(字符串匹配)(AcWing)
KMP算法(字符串匹配)(AcWing)
91 0
|
6月前
|
分布式计算 测试技术 索引
【数位dp】【动态规划】【KMP】1397. 找到所有好字符串
【数位dp】【动态规划】【KMP】1397. 找到所有好字符串
|
6月前
|
JavaScript
代码随想录 Day48 动态规划16 T647 回文子串 T516最长回文子序列
代码随想录 Day48 动态规划16 T647 回文子串 T516最长回文子序列
42 0
|
6月前
KMP算next数组(2023 _ 7 _ 23 )笔记
KMP算next数组(2023 _ 7 _ 23 )笔记
42 0
|
C语言
next数组的两种求法详解及完整代码
求字符串的next数组: 方法一: 这里我们将next数组第1,2位分别设为0,1(还有-1,0这种设法,这里先将其设为0,1若有需要再减一即可) 后面求解每一位的next值时,根据前一位进行比较。 从第三位开始,将前一位与其next值对应的内容进行比较, 如果相等,则该位的next值就是前一位的next值加上1; 如果不等,向前继续寻找next值对应的内容来与前一位进行比较, 直到找到某个位上内容的next值对应的内容与前一位相等为止, 则这个位对应的值加上1即为需求的next值; 如果找到第一位都没有
365 0
next数组的两种求法详解及完整代码
|
算法
next数组(详细求法)
next数组(详细求法)
184 0
|
算法
看了这个你基本就会算kmp算法的next数组了
看了这个你基本就会算kmp算法的next数组了
Leecode 5. 最长回文子串
Leecode 5. 最长回文子串
43 1
|
算法 C语言
对于KMP的next数组的新发现,好像我们并不用回溯
对于KMP的next数组的新发现,好像我们并不用回溯
44 0