(Day16) 算法复健运动for蓝桥杯-KMP(看猫片)
(Day16) 算法复健运动for蓝桥杯-KMP(看猫片)
- next数组
next数组是子串的每一位对应的一个数字
这个数字是这一位的前面所有位置的最长真前后缀
最长真前后缀:
最前面和最后面相同的字母的长度
J | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
模式串 | a | b | c | a | a | b | b | c | a | b |
next[j] | -1 | 0 | 0 | 0 | 1 | 1 | 2 | 0 | 0 | 1 |
0位置一定为-1
1位置一定为0
举例:
上表中的位置6:a b c a a b (0~5的数)
next数组为2
先不说代码 没有啥用
- KMP的原理就是。。就是。。就是
假如子串是ababc
匹配串是abababc
开始的时候
情况1:
abababc
ababc
情况2:
abababc
ababc
在黄的这个地方失配了 j需要跳,next[4]=2,发现j跳到了2,子串2的位置是a(第二个a)
跳到了a,就是要a,与匹配串失配的地方继续比较(情况2)
那么问题来了,为什么要跳到a
情况1中 a和c失配 但是同时也说明子串中的abab匹配上了
跳到情况2之后,还是ab和ab匹配在了一起
因为算c(子串的c)的前缀后缀的时候,就是算的abab(2)
所以。。所以
kmp的next数组作用就是:
重点!!!!!!!!!!!
当某一位匹配失败时,也同时说明它的前面所有字母的后缀,是匹配上的,所以为了节省时间,把它的前缀移动到对齐匹配串原来匹配上后缀的位置(后缀前缀的字母是相同的,位置不同)
得到next数组的代码:
void getnext()
{
int len=strlen(s2);
int i=0;
int j=-1;//j=-1是一个初始值这时候j在最前端,这个时候预备计算真前后缀的长度(预备状态)
Next[0]=-1;//0位置必须是-1
while(i<len)
{
if(j==-1||s2[i]==s2[j])//这个条件很有意思,见下面的解释
{
Next[++i]=++j;
}
else
{
j=Next[j];
}
}
}
解释:
先解释第二个
比如说子串是ababc
a b a b c 这个b的前面字母是aba,next数组是1,i=3,j=1(根据next数组得到的)
对s2[i]==s1[j]条件的解释:
相当于想填Next[i+1]位置的数值,找不到就一直往前找
i的值是计算的那一位的前一位(计算 next数组)j的值是匹配上的前缀的后一位(或者说预备前缀,就是j指向的那个值正在判断是不是能匹配上的前缀)这一点说的不严谨,大致知道意思就行
a b a b c 现在来看c,上一行aba中,a和a已经相等了,如果想增长这个前后缀的长度,i=3,j=1指向的字母必须相等(两个b)
这个就是s1[i]=/=s2[j]是怎么来的了
a b a(下一位必须是b才能增长)
a b a b
j==-1的情况,就是找不到前缀了,无奈之举,所以Next[++i]=++j;//++j就是0
情况3:babbbabbcd 被刷成黄色的c 算完它的next数组时,i=8,j=4;于是算d的next的时候 会遭遇判断(c==b)? 答案是否定的
babbbabbcd(看!kmp)
情况4:babbbabbcd 答案是否定之后,于是实施了第二个条件,j跳到a处与c对线
babbbabbcd (看!kmp)
第一次看的时候,就一直有个疑问,为什么构造next数组的过程中要调用next数组????(禁 止 套 娃)
但其实自己跑一遍这个过程的时候。。。会发现。。
情况3中 b和c失配(对,我们不用不相等,我们用失配)
其实构造next,也是一种kmp算法
有一位失配了,就跳到之前已经匹配上的下一位,继续匹配
不过区别是kmp是两个串匹配,next构造是同一个字符串前后的部分匹配
就拿情况3举例:
b和c失配说明babb和后面的babb匹配上了!!
所以,前面的babb就跳了位置,具体怎么跳的,上面讲kmp讲过
开头的那个b就必匹配上(KMP)
然后让a和c对线 如果它俩都是a,那结果就是2了,如果不是。。。(后面的没必要讲,没太大意义)
KMP的代码不用讲了,很好理解,上面已经讲过原理了
模板代码:(判断是不是子串)
#include<stdio.h>
#include<string.h>
char s1[100009];
char s2[34];
int Next[39];
void getnext()
{
int len=strlen(s2);
int i=0;
int j=-1;
Next[0]=-1;
while(i<len)
{
if(j==-1||s2[i]==s2[j])
{
Next[++i]=++j;
}
else
{
j=Next[j];
}
}
}
bool kmp()
{
int len1=strlen(s1);
int len2=strlen(s2);
int i=0,j=0;
while(i<len1)
{
if(j==-1||s1[i]==s2[j])//匹配上就往后移
{
i++;
j++;
}
else//没匹配上就跳j
{
j=Next[j];
}
if(j==len2)//如果j越界了,就匹配上了
{
return true;
}
}
return false;
}
int main()
{
scanf("%s%s",s1,s2);
getnext();
if(kmp())printf("是子串\n");
else printf("不是\n");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现