KMP算法,字符串算法,在CSDN博客这里,有很多人写的很好,非常地详细,让人自叹不如。然而我很懒,不想看太多文字,只想通俗地理解这KMP,所以写下这篇文章,文章很简陋,只是基本搞懂KMP的原来,没有去深究它,如果这文章对大家没有帮助,请忽略。
字符串hash是指将一个字符串s映射为一个整数,使得该整数可以尽可能唯一地代表字符串s。那么在一定程度上,如果两个字符串转换成地整数相等,就可以认为这两个字符串相同。
直达链接:原题地址
#include
#include
#include
#include
using namespace std;
const int Mod = 1e9+7;
const int p = 1e7+19;
vector<int>arr;
long long hashfunc(string str){
long long Hash = 0;
for(int i=0;i<str.size();i++){
Hash = (Hash*p + str[i] - 'a')%Mod;
}
return Hash;
}
int main(){
string str;
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>str;
long long Hash=hashfunc(str);
arr.push_back(Hash);
}
sort(arr.begin(),arr.end());///字符串排序
int ans = 0;
for(int i=0;i<arr.size();i++)
if(i==0||arr[i]!=arr[i-1]){
ans++;
}
cout<<ans<<endl;
return 0;
}
在了解KMP算法之前我们先了解一下一个暴力匹配算法,对于字符串的匹配问题我们很多人都可能一开始想到的是暴力,没错!!!如果题目数据量不大,可以选择这个算法:
暴力匹配核心代码:
int stringMatch(char *text,char *pattern){
int tlen = strlen(text);
int plen = strlen(pattern);
int i=0;
int j=0;
while(i<tlen&&j<tlen){
if(text[i]==pattern[j]){
i++;
j++;
}
else{
i=i-j+1;
j=0;
}
if(j==plen) return i-j;///匹配成功,返回模式串在主串中的位置
else return -1;///匹配失败
}
}
暴力匹配算法的复杂度为O(nm),所以如果数据量比较大的话,这种算法就无法接受了。
b站上讲得比较通俗易懂的两个视频:
1.字符串匹配算法1
2.字符串匹配算法2
Knuth-Morris-Pratt字符串查找算法,简称为"KMP算法",这个算法由Donald Knuth、Vaughan Pratt、James H. Morris三人共同发现的。这算法用于判断字符串pattern是否为text的字串。
在学KMP算法之前,我们先来学习一个next数组,这个数组对于实现KMP算法非常重要,等同于求出next数组就差不多可以实现字符串匹配了。如:子串s[0…i],其长度为k+1的前缀和后缀分别为s[0…k]和s[i-k…i]。nexi[i]就是使子串s[0…i]的前缀s[0…k]等于后缀s[i-k…i]的最大的k(最大公共前后缀的长度)。
虽然上面图让我们明白求next数组的基本原来,但是我们在程序中怎么去编代码一一地找出最大公共前后缀地长度呢?
第①步:j=0,i=1→s[1] ! = s[0],回溯到 j=0→next[1]=j=0。
第②步:当前j=0,i=2→回溯s[2]!=s[1],s[2]==s[0],跳出循环,j=0→next[2]=j+1=1。
第③步:当前j=1,i=3→s[3]!=s[1],s[3]!=s[0],j=0→next[3]=j=0。
第④步:当前j=0→s[4]==s[0],j=0跳出循环→next[4]=j+1=1。
第⑤步:当前j=1→s[5]==s[1],j=1跳出循环→next[5]=j+1=1+1=2。
第⑥步:当前j=2→s[6]==s[2],j=2跳出循环→next[6] = j+1 = 2+1=3。
第⑦步:当前j=3→s[7]!=s[3],s[7]==s[0],j=0,跳出循环→next[7]=j+1=1。
获取数组next的值:
void getNext(char s[]){
int len = strlen(s);
int j = 0;
next[0] = 0;
for(int i = 1;i<len;i++) {///由于第一个字符肯定next为0(因为最大公共前后缀不能为本身),所以从1开始
while(s[i]!=s[j]&&j!=0) j = next[j];///j=0跳出循环
if(s[i]==s[j]) j++;
next[i] = j;
}
}
kmp算法实现:
bool kmp(char *text,char *pattern){
int n = strlen(text);
int m = strlen(pattern);
getNext(pattern);
int j=0;
for(int i=0;i<n;i++){
while(j!=0&&text[i]!=pattern[j]) j = next[j];
if(text[i]==pattern[j]) j++;
if(j==m-1) return true;
}
return false;
}
KMP的匹配是从模式串的开头开始匹配的,而1977年,德克萨斯大学的Robert S. Boyer教授和J Strother Moore教授发明了一种新的字符串匹配算法:Boyer-Moore算法,简称BM算法。该算法从模式串的尾部开始匹配,且拥有在最坏情况下O(N)的时间复杂度。比KMP算法的实际效能高。
BM算法定义了两个规则:
☞ 坏字符规则:
第①种情况: 模式串中不存在S字符:S与N不匹配,S为坏字符。
第②种情况: 模式串与文本串都存在A字符,A与N不匹配,A为坏字符。
☞ 好后缀规则:
Y与A不匹配,需要移动:
Subday算法比BM算法效能更快,Sunday算法由Daniel M.Sunday在1990年提出,它的思想跟BM算法很相似:Sunday算法是从前往后匹配,在匹配失败时关注的是文本串参加匹配的最末位字符的下一个字符。如果该字符没有在匹配中出现则直接跳过,即移动步长=模式串长度+1;否则,同BM算法一样移动步长模式串中最右端的该字符到末尾的距离+1。
1.从头到尾彻底理解KMP(2014年8月22日版)
2.《算法笔记》
3.http://blog.chinaunix.net/uid-22237530-id-1781825.html