在暴力匹配两个字符串时,如果匹配失败,文本串跳到刚开始匹配的下一位置,模式串跳到开头。
暴力匹配具体步骤:
- 如果匹配成功,则++i,++j,继续匹配下一字符;
- 如果匹配失败,i=i-(j-1),j=0,即i回溯,j置0(从头再来)
int violentMatch(const string& s,const string& p) { int sLen=s.size(); int pLen=p.size(); int i=0,j=0; while(i<sLen&&j<pLen) { if(s[i]==p[j]) { ++i; ++j; } else { i=i-j+1; j=0; } } return j==pLen?i-j:0; }
为了减少时间复杂度,在kmp算法中,匹配失败时文本串不动,移动模式串来进行下次匹配,模式串的移动位置靠next数组来决定。
next 数组各值的含义:代表当前字符之前的字符串中,有多大长度的相同前缀后缀。例如如果next [j] = k,代表j 之前的字符串中有最大长度为k 的相同前缀后缀。此也意味着在某个字符失配时,该字符对应的next 值会告诉你下一步匹配中,模式串应该跳到哪个位置(跳到next [j] 的位置)。如果next [j] 等于0或-1,则跳到模式串的开头字符,若next [j] = k 且 k > 0,代表下次匹配跳到j 之前的某个字符,而不是跳到开头,且具体跳过了k 个字符。
kmp具体步骤:
- 如果j==-1(模式串跳到第一个字符)或 当前位置模式串和文本串匹配成功:则++i,++j;
- 如果next[j]>0,则模式串跳到以j结尾字符串的最长前缀和最长后缀相等的位置,即j=next[j]。
#include <iostream> #include <vector> using namespace std; class Kmp { private: vector<int> next;//next[i],以i结尾(不包含i)的最长前缀和后缀相等的值 private: void getNext(const string& p)//求模式串的next数组 { int pLen=p.size(); next.resize(pLen,0); next[0]=-1; next[1]=0; int flag=0; for(int i=2;i<pLen;) { if(p[flag]==p[i-1]) next[i++]=++flag; else if(flag>0) flag=next[flag]; else next[i++]=0; } return ; } public: int kmp(const string& s,const string& p) { getNext(p); int i=0,j=0; int sLen=s.size(),pLen=p.size(); while(i<sLen&&j<pLen) { if(s[i]==p[j]) { ++i; ++j; } else if(next[j]==-1)//j移动到模式串的第一个字符,即j==0 { ++i; } else { j=next[j]; } } //与下列等效 #if 0 while(i<sLen&&j<pLen) { if(j==-1||s[i]==p[j]) { ++i; ++j; } else { j=next[j]; } } #endif // 0 return j==pLen?i-j:0; } }; int main() { Kmp k; cout<<k.kmp("BBC ABCDAB ABCDABCDABDE","ABCDABD")<<endl; return 0; }