求解字符串匹配问题---KMP算法

模式串匹配,给定一个需要处理的文本串和一个需要再文本串中搜索的模式串,查询再该文本串中,给出的模式串的出现有无,次数,位置等
KMP的精髓在于,对于每次匹配失败后,我们都不会重新开始枚举,而是根据我们已经得知的数据,从某个特定的位置开始匹配,对于模式串的每一个,都有唯一的"特定变化位置",在这个匹配失败后的特定变化位置可以帮助我们利用已有的数据不用从头匹配,从而节约时间
要求KMP我们有两个步骤
1.求模式串的next数组

void getnext(){
int lenss=strlen(ss);
int i=0,j=-1;
Next[0]=-1;
while(i<lenss){
    if(j==-1 || ss[i] == ss[j]){//当j为-1 或者 ss[i] == ss[j] 时,我们把当前位置i的下一个,即 i+1 的要进行比较的位置变为 j+1
        Next[++i]=++j;
    }
    else {//匹配失败,j变为 他之前一个下一次要匹配的位置
        j = Next[j];
    }
}
}

2.根据题意求KMP

bool KMP(){//匹配查找子串
getnext();
int i=0,j=0;//i 代表文本串匹配位置 , j 代表模式串匹配位置
int lenstr = strlen(st);
int lenss = strlen(ss);
while(i < lenstr){
    if(j==-1 || st[i] == ss[j]){
        i++;
        j++;
    }
    else {
        j = Next[j];
    }
    if(j == lenss){
       return true;
    }
}
return false;
}

完整代码

#include<bits/stdc++.h>
using namespace std;
char st[1000010];
char ss[1000010];
int Next[1000010];
void getnext();
bool KMP(){//匹配查找子串
getnext();
int i=0,j=0;//i 代表文本串匹配位置 , j 代表模式串匹配位置
int lenstr = strlen(st);
int lenss = strlen(ss);
while(i < lenstr){
    if(j==-1 || st[i] == ss[j]){
        i++;
        j++;
    }
    else {
        j = Next[j];
    }
    if(j == lenss){
       return true;
    }
}
return false;
}
void getnext(){//得到next数组
int lenss=strlen(ss);
int i=0,j=-1;
Next[0]=-1;
while(i<lenss){
    if(j==-1 || ss[i] == ss[j]){//当j为-1 或者 ss[i] == ss[j] 时,我们把当前位置i的下一个,即 i+1 的要进行比较的位置变为 j+1
        Next[++i]=++j;
    }
    else {//匹配失败,j变为 他之前一个下一次要匹配的位置
        j = Next[j];
    }
}
}
int main(){
scanf("%s%s",st,ss);
//判断是否匹配到当前字符串
if(KMP()){
    printf("在文本串 %s 中,匹配到了子串 %s",st,ss);
}
else {
    printf("在文本串 %s 中,没有找到子串 %s",st,ss);
}
return 0;
}

题型(单个字符串匹配问题)
1.有无子串,出现了几次, 出现的位置
2.查找当前串的循环子串,就是找循环节,即是求最长公共前后缀
abcabcab
最长公共前后缀为5(abcab)
例如:
给你一个字符串 s1 ,它是由某个字符串 s2 不断自我连接形成的,但是字符串 s2 是不确定的,现在只想知道它的最短长度是多少
我们呢就只需要求它的next数组,因为后面的和前面的相匹配了,有循环,i就会不断地指向前面匹配到的j,(i 和 j 等距滑动),即next[i]!= 0,最后答案就是 n-next[n]

#include<bits/stdc++.h>
using namespace std;
char st[1000010];
char ss[1000010];
int Next[1000010];
void getnext(){
int lenss=strlen(ss);
int j=-1,i=0;
Next[0]=-1;
while(i<lenss){
    if(j==-1||ss[i]==ss[j]){
        Next[++i] = ++j; //如果当前位置匹配或者j==-1,Next就存上i的下一个就与j的下一个匹配
    }
    else
        j = Next[j];//不匹配回溯
}
}
int main(){
int n;
scanf("%d",&n);
scanf("%s",ss);
getnext();
printf("%d ",n-Next[n]);
return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值