#include
#include
using namespace std;
int* GetNext(char* p);
//暴力匹配
int ViolentMatch(char* s,char* p) {
int sLen = strlen(s);
int pLen = strlen(p);
int i = 0, j = 0;
while (i < sLen && j < pLen) {
if (s[i] == p[j])
{
i++;
j++;
}
else
{
i = i - j + 1;
j = 0;
}
}
if (j == pLen)
return i - j;
return -1;
}
//KMP
int KmpMatch(char* s,char* p) {
int sLen = strlen(s);
int pLen = strlen(p);
int i = 0, j = 0;
int* next = GetNext(p);
//cout << sLen << ' ' << pLen << endl;
while (i < sLen && j < pLen) {
if (j == -1 || s[i] == p[j]) {
i++;
j++;
}
else
{
j = next[j];
}
}
if (j == pLen)
return i - j;
return -1;
}
int* GetNext(char *p){
int pLen = strlen(p);
int* next = new int[pLen];
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1)
{
if (k == -1 || p[k] == p[j])
{
//next[++j] = ++k;
j++; k++;
if (p[j] == p[k]) {
next[j] = next[k];
}
else
{
next[j] = k;
}
}
else
{
k = next[k];
}
}
return next;
}
//Sunday算法
const int maxNum = 100;
int SundayMatch(char* s, char* p) {
int sLen = strlen(s);
int pLen = strlen(p);
int next[maxNum] = { 0 };
int n = 0;
for (n; n < maxNum; n++) {
next[n] = pLen + 1;
}
for (n = 0; n < pLen; n++) {
next[p[n] - 'A'] = pLen - n;
}
int pos = 0;
while (pos < (sLen - pLen+1)) {
int i = pos;
int j = 0;
for (j; j < pLen; j++,i++) {
if (p[j] != s[i]) {
pos += next[s[pos + pLen] - 'A'];
break;
}
}
if (j == pLen) {
return pos;
}
}
return -1;
}
int main()
{
char s[] = "BBC ABCDAB ABCDABCDABDE";
char p[] = "ABCDABD";
cout << s << endl;
cout << p << endl;
//int v = ViolentMatch(s,p);
//int v = KmpMatch(s, p);
int v = SundayMatch(s, p);
cout << "匹配位置:" << v << endl;
}
KMP:
KMP算法的关键是在匹配失败时,确定下一次匹配的位置,设next[j]=k,表示当模式串P中第j个字符与母串T相应字符不匹配时,模式串P中应当由第K个字符与目标串中刚不匹配的字符对齐继续进行比较。
给定主串S,子串P,i、j分别代表两串的位置
接下来就剩下Next数字的推导了。
求Next数组:
求next数组是一个递推的过程
已知next[0,1,2…j],求next[j+1]。
设k为next[j]
如果p[k]==p[j],next[j+1]=next[j]+1(因为p[j]之前的字符串最大前后缀为k,所以p[0,1,2…k-1]==p[j-k,j-k+1,…j-1],若p[k]==p[j],则p[0,1,2…k-1,k]==p[j-k,j-k+1,…j-1,k]成立,所以p[j+1]之前的字符串的最大前后缀为next[j]+1)
反之,若p[k]!=p[j]
P[k]则向前递归,是否存在p[i]==p[j],i为next[k],重复迭代.
p[0,1,2…k-1]==p[j-k,j-k+1…j-1]且p[0,1,2…i]==p[k-I,k-i+1…k-1],所以p[0,1,2…i-1]==p[j-I,j-i+1…j-1]
如果p[i]==p[j],则next[j+1]next[i]+1next[next[k]]+1,接下来重复如上流程直到p[前缀]p[j]或者p[前缀]-1
复杂度:如果主串长度为n,字串长度为m,匹配时间为O(n),next时间为O(m),总体时间为O(m+n)。
Sunday:
Sunday算法是从前往后匹配,在匹配失败时关注的是文本串中参加匹配的最末位字符的下一位字符。
如果该字符没有在模式串中出现则直接跳过,即移动位数 = 匹配串长度 + 1;
否则,其移动位数 = 模式串中最右端的该字符到末尾的距离+1。