拓展kmp的作用:
①字符串匹配, kmp所能做到的字符串配类型题exkmp都能做;
(拓展kmp 解决 一个字符串在另一个字符串中出现多少次更方便)
推荐推理博客:https://blog.csdn.net/dyx404514/article/details/41831947
个人对理解的exkmp:
①首先要知道:
ex[] 数组 存的是 主串 以i开头的子串与模式串的最大公共前缀。
nex[] 数组存的是 模式串以i开头的子串与模式串的最大公共前缀。
② 如何获取ex数组:
首先我们通过for 一遍 可以求出ex[0]
对于(1,slen)要在前面的基础上求
/********
学习方法:
在假设 nex 配好 的情况下, 先学 exkmp 这个函数;再回来学习 get_next;
函数get_next函数和exkmp原理一样;只不过前者自己跟自己的后缀找最大公共前缀 后者是自己 跟别的串的后缀 找最大公共前缀
最好不要看图学习 看公式推理学
***********/
#include<cstdio>
#include<cstring>
const int maxn = 1e6+50;
int nex[maxn];//储存 模式串 各后缀 与模式串 的 最大公共前缀
int ex[maxn];// 储存 主串 各后缀 与模式串 的 最大公共前缀
void Get_next(char *ptr)
{
int i = 0;
int plen = strlen(ptr);
nex[0] = plen; //计算nex[0], nex[0] 就等于模式串的长度
while(i+1 <plen && ptr[i] == ptr[i+1])// 直接匹配 计算nex[1]
{
i++;
}
nex[1] = i;
int po;
po = 1; //初始化 po 的位置
for(int i = 2;i < plen;i++)
{
int p = nex[po]+po;
if(nex[i-po] +i<p) // 第一种情况
nex[i] = nex[i-po];
else
{
int j = p - i;
if(j < 0)j = 0;
while(i+j < plen&& ptr[j] == ptr[i+j])
{
j++;
}
nex[i] = j;
po = i;
}
}
}
void Exkmp(char *str, char *ptr)
{
Get_next(ptr);//计算nex数组
int slen = strlen(str);
int plen = strlen(ptr);
int i = 0;
while(i < slen && i <plen&& str[i] == ptr[i])i++;//计算ex[0]
ex[0] = i;
int po;
po = 0; //po表示 前面匹配最远距离 的开头
for(int i = 1;i < slen;i++)//计算ex[1] 到ex[slen-1]
{
int p = ex[po]+po; //p 表示 前面匹配 最远距离的失配位置
if(nex[i-po]+i < p) // 第一种情况 如果 nex[i-po]长度 不超过 p-i长度, 直接得到ex[i]
{
ex[i] = nex[i-po];
}
else // 第二种情况 超过 了
{
int j = p-i; // 模式串 结下了 要匹配的位置
if( j < 0)j = 0; //第二种 情况① i > p的情况(之前匹配的就没有帮助了) j要0从新开始匹配
while(i+j < slen && j < plen && str[i+j] == ptr[j]) // 接着匹配 剩余部分
{
j++;
}
ex[i] = j;
po = i;//更新 po位置
}
}
}
杭电模板题http://acm.hdu.edu.cn/showproblem.php?pid=1686
/********
学习方法:
在假设 nex 配好 的情况下, 先学 exkmp 这个函数;再回来学习 get_next;
函数get_next函数和exkmp原理一样;只不过前者自己跟自己的后缀找最大公共前缀 后者是自己 跟别的串的后缀 找最大公共前缀
最好不要看图学习 看公式推理学
***********/
#include<cstdio>
#include<cstring>
const int maxn = 1e6+50;
int nex[maxn];//储存 模式串 各后缀 与模式串 的 最大公共前缀
int ex[maxn];// 储存 主串 各后缀 与模式串 的 最大公共前缀
void Get_next(char *ptr)
{
int i = 0;
int plen = strlen(ptr);
nex[0] = plen; //计算nex[0], nex[0] 就等于模式串的长度
while(i+1 <plen && ptr[i] == ptr[i+1])// 直接匹配 计算nex[1]
{
i++;
}
nex[1] = i;
int po;
po = 1; //初始化 po 的位置
for(int i = 2;i < plen;i++)
{
int p = nex[po]+po;
if(nex[i-po] +i<p) // 第一种情况
nex[i] = nex[i-po];
else
{
int j = p - i;
if(j < 0)j = 0;
while(i+j < plen&& ptr[j] == ptr[i+j])
{
j++;
}
nex[i] = j;
po = i;
}
}
}
void Exkmp(char *str, char *ptr)
{
Get_next(ptr);//计算nex数组
int slen = strlen(str);
int plen = strlen(ptr);
int i = 0;
while(i < slen && i <plen&& str[i] == ptr[i])i++;//计算ex[0]
ex[0] = i;
int po;
po = 0; //po表示 前面匹配最远距离 的开头
for(int i = 1;i < slen;i++)//计算ex[1] 到ex[slen-1]
{
int p = ex[po]+po; //p 表示 前面匹配 最远距离的失配位置
if(nex[i-po]+i < p) // 第一种情况 如果 nex[i-po]长度 不超过 p-i长度, 直接得到ex[i]
{
ex[i] = nex[i-po];
}
else // 第二种情况 超过 了
{
int j = p-i; // 模式串 结下了 要匹配的位置
if( j < 0)j = 0; //第二种 情况① i > p的情况(之前匹配的就没有帮助了) j要0从新开始匹配
while(i+j < slen && j < plen && str[i+j] == ptr[j]) // 接着匹配 剩余部分
{
j++;
}
ex[i] = j;
po = i;//更新 po位置
}
}
}
int main()
{
char s1[maxn], s2[maxn];
int T;
scanf("%d", &T);
while(T--)
{
scanf("%s %s", s2, s1);
int f = 0;
Exkmp(s1, s2);
int len1 = strlen(s1), len2 = strlen(s2);
for(int i = 0; i < len1; i++)
{
if(ex[i] == len2)
{
f++;
}
}
printf("%d\n", f);
}
return 0;
}