KMP字符串匹配算法
该算法只与模式串(字串)有关。BF算法匹配会造成很多没有必要的回溯。kmp算法就是避免这些没有必要的回溯。
当模式串主串匹配到某个位置的时候失配,假设该模式串下标为j,主串下标为i,则模式串下标0~j-1都是匹配的,按照BF算法,我们要将模式串回溯到首位,主串回溯到第二位重新匹配。
情况一:
如果模式串完全没有相同的元素,则前面匹配的全匹配,而模式串之间没有相同的元素,回溯过去也不可能匹配
例如:
i l o v e T H i o v e T H K
i l o v e T H K
此时,i与Q失配,如果仅回溯一位
i l o v e T H i o v e T H K
i l o v e T H Q
不可能匹配,我们完全可以直接回溯到i与K来匹配
i l o v e T H i l o v e T H K
i l o v e T H K
情况二:
如果模式串有相同的元素,也有不必要的回溯。
例如:
w w w x h h
w w x
此时w与x失配,不能按照情况一,直接将1号元素来与失配元素匹配。而是
w w w x h h
w w x
按照KMP算法来解答:
回溯的时候不是直接一位一位的回溯,而是按照next数组的指导来回溯。
求next数组:
TS串:模式串与主串失配位置前的元素就是TS串。
TC串:TS串能自从后包含(即从后面往前看也是匹配的)TC串(TC串是TS串的字串),TC串的第一个字符是模式串的第一个字符。TC短于TS。TC可能不为1
此时的next数组的第n个元素激素该n对应下的|TC|+1;
注意,为了保证不丢失可能的匹配情况,必须选择最长的TC串。
求TC串:
先求出TS,分为TS1,TS2,将TS1设为主串,TS2为模式串,一步一步右移TS2,判断竖着相对应的元素是否完全相等,相等的就是最长TC串。
例:
TS1:A B A
TS2: A B A
-->
TS1:A B A
TS2: A B A
-->
TC=A
next[4]=|TC|+1=2
C++实现源码:
#include <iostream>
#include <string>
class FstringASstring{
public:
std::string Fs;//主串(父串)
std::string Ss;//模式串 (子串)
int *next;
int *nextVal;
FstringASstring();
bool KMP();
void getNext();
void getNextVal();
};
int main(){
FstringASstring FAS;
if(FAS.KMP()){
std::cout<<"Ss是Fs的子字符串!\n";
}else{
std::cout<<"Ss不是Fs的子字符串!\n";
}
return 0;
}
FstringASstring::FstringASstring(){
Fs="abcdefg";
Ss="defg";
next=new int(Ss.length());
nextVal=new int(Ss.length());
}
void FstringASstring::getNext(){
int indexS=0;//索引TS串中字符
int indexC=-1;//索引TC串中字符
next[0]=-1;
while(indexS<Ss.length()-1){
if(indexC==-1 //TC串与TS串完全没有重合,跳转到模式串的第一位去
//TC:1...indexC
//TS:1...indexS
//TC与TS最大重合了,next[indexS+1]就有结果了
||Ss[indexS]==Ss[indexC]){
++indexC;
++indexS;
next[indexS]=indexC;
}else{
indexC=next[indexC];//利用已得的next,对TC串进行跳转
}
}
}
bool FstringASstring::KMP(){
getNextVal();
int i=0;//主串下标
int j=0;//模式串下标
while(i<(int)Fs.length() && j<(int)Ss.length())
{
if(j==-1 || Fs[i]==Ss[j])
{
i++;
j++;
}else
{
j=nextVal[j];
}
}
if(j == (int)Ss.length())
{
return true;
}
else
{
return false;
}
}
void FstringASstring::getNextVal(){
int indexS=0;//索引TS串中字符
int indexC=-1;//索引TC串中字符
nextVal[0]=-1;
while(indexS<Ss.length()-1){
if(indexC==-1 //TC串与TS串完全没有重合,跳转到模式串的第一位去
//TC:1...indexC
//TS:1...indexS
//TC与TS最大重合了,next[indexS+1]就有结果了
||Ss[indexS]==Ss[indexC]){
++indexC;
++indexS;
//跳转后的字符,与当前字符不同,则找最终跳转到 位置
if(Ss[indexS]!=Ss[indexC]){
nextVal[indexS]=indexC;
}
else{
//nextVal[indexC]已经求得了
nextVal[indexS]=nextVal[indexC];
}
}else{
indexC=nextVal[indexC];//利用已得的next,对TC串进行跳转
}
}
}