kmp算法根据我自己的理解就是,kmp算法就是来判断两个字符串存不存在包含关系。也就是一个主字符串a,副字符串b,判断字符串a中是否有子串,以及有多少子串或者子串的位置,等等这些问题。
总体思想就是主串和子串进行匹配时,子串到你匹配失败的那一个点之前的几个位置是否有与开头的字符一样,如果有就返回开头与匹配结尾失败的地方样位置的下一个,如果不一样就移动到首位。
我理解的KMP算法的思想就是先构造next数组,通过next来是实现减少时间复杂度,那么next数组如何构造呢。先附一张图
就是先对子串进行匹配,什么叫做对子串进行匹配就是看看子串中是否有重的,子串从一个字符开始,然后两个。。。。。用next来存子串中重复重复出现的位置,图中的数字是前i个字符的意思。子串对于然后就是对子串和主串同时操作了,如果主串和子串中第一个不匹配,子串一直保持在首位不会移动,主串的字符不断移动,当子串和主串匹配时,主串和子串一起移动,直到遇不匹配的时候,子串会再次移动到主串的位置,主串接着往下移动,如果子串和主串匹配成功的时候,如果还想接着匹配主串接着移动,子串就重新回到首位,接着上述操作进行匹配。举个例子:
Oulipo
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1686
该题就用KMP算法来找主串中子串出现的次数
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define N 10005
string a, b;
int next[N];
int sum;
void Getnext(string a){//进行构造next数组
int i = 0, j = -1;
next[0] = -1;
int len = a.size();
while(i < len){
while(j != -1 && a[i] != a[j])j = next[j];//如果没有一样的就返回首位,否则就子串中i和j向下移动一个位置
i++;
j++;
next[i] = j;
}
}
void KMP(string a, string b){
int n = a.size(), m = b.size();
int i = 0, j = 0;
Getnext(b);
while(i < n){
while(j != -1 && a[i] != b[j])j = next[j];//如果没有一样的就返回首位,否则就子串和主串向下移动一个位置
i++;
j++;
if(j >= m){//当匹配成功的时候返回首位
sum ++;
j = next[j];
}
}
}
int main(){
int T;
cin>>T;
string a, b;
while(T--){
cin>>a>>b;
sum = 0;
KMP(b,a);
cout<<sum<<endl;
}
return 0;
}
入侵检测
链接:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=1309
本题就是利用KMP算法查找主串中是否含有子串
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define N 10005
string a, b;
int ne[N];
int sum;
void Getnext(string a){//进行构造next数组
int i = 0, j = -1;
ne[0] = -1;
int len = a.size();
while(i < len){
while(j != -1 && a[i] != a[j])j = ne[j];//如果没有一样的就返回首位,否则就子串中i和j向下移动一个位置
i++;
j++;
ne[i] = j;
}
}
int KMP(string a, string b){
int n = a.size(), m = b.size();
int i = 0, j = 0;
Getnext(b);
int flag = 0;
while(i < n){
while(j != -1 && a[i] != b[j])j = ne[j];//如果没有一样的就返回首位,否则就子串和主串向下移动一个位置
i++;
j++;
if(j == m)//当匹配成功的时候返回首位
{
flag = 1;
}
}
if(flag )return i - j;
else return -1;
}
int main(){
string a, b;
while(cin>>a>>b){
cout<<KMP(a, b)<<endl;
if(KMP(a, b) == 1)cout<<"yes"<<endl;
else cout<<"no"<<endl;
}
return 0;
}
下面的代码就是对上面的进行补充,就是返回第一个找到的位置。
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define N 10005
string a, b;
int ne[N];
int sum;
void Getnext(string a){//进行构造next数组
int i = 0, j = -1;
ne[0] = -1;
int len = a.size();
while(i < len){
while(j != -1 && a[i] != a[j])j = ne[j];//如果没有一样的就返回首位,否则就子串中i和j向下移动一个位置
i++;
j++;
ne[i] = j;
}
}
int KMP(string a, string b){
int n = a.size(), m = b.size();
int i = 0, j = 0;
Getnext(b);
int flag = 0;
while(i < n){
while(j != -1 && a[i] != b[j])j = ne[j];//如果没有一样的就返回首位,否则就子串和主串向下移动一个位置
i++;
j++;
if(j == m)//如果匹配成果就break掉,做一个标记,
{
flag = 1;
break;
}
}
if(flag )return i - m;//当检测有这个标记的时候证明,已经匹配成功
else return -1;
}
int main(){
string a, b;
while(cin>>a>>b){
cout<<KMP(a, b)<<endl;
}
return 0;
}