一、算法思想
空间换时间
把模式Pattern和文本Text的开头字符对齐,从模式的最后一个字符开始比较,如果尝试比较失败了,把模式向后移。向后移动的距离是查已构造好的移动表获得。每次尝试过程中比较是从右到左的。
移动表的构造可以仅使用模式串Pattern,设字符c是文本Text中对齐模式串的最后一个字符的元素,那么在每次匹配失败时,就根据字符c查找移动表来确定移动距离(不管字符c和模式串的最后一个字符是否匹配)。对于每个字符c,可用以下公式算出移动距离:
二、代码
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
//构造移动表(仅构造了模式串中存在的字符,模式串中不存在的字符移动距离为模式串长度)
unordered_map<char,int> buildTable(string& partern){
unordered_map<char,int> moveTable;
int m = partern.size();
for(int i=0;i<m-1;++i){
moveTable[partern[i]] = m-1-i;
}
return moveTable;
}
//Horspool字符串匹配. 返回第一个匹配子串最左端字符的下标 or -1
int Horspool(string& partern,string& text){
unordered_map<char,int> moveTable = buildTable(partern);
int m = partern.size(), n = text.size();
int i = m-1;
while(i<n){
int k = 0; //匹配字符的个数
//从右向左进行匹配
while(k<m && partern[m-1-k]==text[i-k]){
k+=1;
}
if(k==m){//模式全部匹配成功
return i-m+1;
}
else{ //否则根据移动表进行移动
i = moveTable.find(text[i])!=moveTable.end() ? i+moveTable[text[i]] : i+m;
}
}
return -1;
}
三、KMP算法
vector<int> getNext(string& partern){
int n = partern.size();
vector<int> next(n);
next[0] = -1;
int j = 0,k = -1;
while(j < n-1){
if(k == -1 || partern[j] == partern[k]){
next[++j] = ++k;
}
else{
k = next[k];
}
}
return next;
}
int kmp(string& partern,string& text){
int i = 0; //主串的位置
int j = 0; //模式串的位置
int n = text.size(),m = partern.size();
vector<int> next = getNext(partern);
while(i<n && j<m){
if(j == -1 || text[i] == partern[j]){ //当j为-1时,要移动的是i,j也要归0
i++;
j++;
}
else{
j = next[j];
}
}
if(j==m){
return i-j;
}
else {
return -1;
}
}