之前一直用的kmp来做字符串的匹配题目,直到今日才知道还有sunday这种高效又简单的算法,虽然不能百分之一万的保证准确性,我也没有看sunday算法的相关证明,不过学习了之后确实觉得巧妙,今天我就好好写写这两种算法。
第一 经典的kmp:
kmp的核心就是求next数组,而求next数组,主要就是以下几步:
每一个next[i]的值都代表着i前面的字符的最大回文长度。因此,我们设next[0]=-1,因为位置0前面没有字符,然后设next[1]=0,因为1前面只有0一个字符·,所以为零。
然后我们将一个计数器记零,这是当前最大kmp匹配长度+1,将要求的next的位置指针指向2,然后一路往下,
匹配的规则就是:看当前位置的字符与计数器所在的字符是否相等,因为之前的是最长字符串,已经匹配完了,都相等,如果这个字符也相等,那么当前位置的next值就是上一个next加一。如果不相等,就要把计数器跳到最长字符串的尾端的位置的next,找到最长字符串里面的最长回文字符串,看看是不是相等,直到相等加一,或者计数器跳至负一,游戏结束,将next置零。
现在看实现:
void getnext(vector<int> &next,string str){
next[0]=-1;
next[1]=0;
int pos=2;
int kmp=0;
while(pos<str.size()){
if(str[pos-1]==str[kmp]){ //如果pos前面的位置的字符与他的最大回文的后面那个位置相等
next[pos++]=++kmp;}
else if(kmp>0){
kmp=next[kmp];}
else next[pos++]=0;
}
好了,现在我们有了这个next数组,就可以去推原始字符串了。
int getindexof(string ss,string s){
int size=ss.size();
vector<int>next(s.size());
getnext(next,s);
int i=0;
int j=0;
while(i<size){
if(ss[i]==s[j]){
i++;
j++;
}
else if(next[j]==-1){
i++;
else j=next[j];
if(j==s.size())
return i-j;
}
return -1;
}
如果主串和匹配串字符相同,将他们一起下滑一个单位,如果不同,将匹配串下滑到自己的next值的单位,如果为零表示没有一个一样的,主串往后面滑一位。
现在我们来看看一个更方便快捷的sunday算法:
sunday算法的基本思路就是:给你一个主串和一个匹配串,先匹配一下他们的头看相等不相等,如果相等,后滑一位,匹配下一位,如果不相等,直接在主串上面后滑匹配串长度的位置,然后滑动匹配串看当前字符在匹配串中有没有出现,如果没出现,直接滑动匹配串长度,如果出现,滑动匹配串至与主串一致,然后重新进行一步骤:
int SundaySearch(string text, string pattern) {
int i = 0;
int j = 0;
int k;
int m = pattern.size();
if (text.size() <= 0 || m <= 0) return -1;
for (; i <text.size();) {
if (text[i] != pattern[j]) {
for (k = pattern.size() - 1; k >= 0; k--)
{
if (pattern[k] == text[m])
break;
}
i = m - k;
j = 0;
m = i + pattern.size();
}
else {
if (j == pattern.size()-1)
return i - j;
i++;
j++;
}
}
return -1;
}