如何判斷一個字符串是否是另外一個字符串的子串,咱們第一反應就是indexOf或includes或者用正則,雖然沒有什麼不對,可是仍是須要了解一下字符串匹配是怎麼匹配的。在計算機科學中,Knuth-Morris-Pratt字符串查找算法(簡稱爲KMP算法)可在一個主文本字符串S內查找一個詞W的出現位置。此算法經過運用對這個詞在不匹配時自己就包含足夠的信息來肯定下一個匹配將在哪裏開始的發現,從而避免從新檢查先前匹配的字符。(尷尬了,大一時學的算法,不用就全忘了┭┮﹏┭┮)javascript
設母串,子串
,在傳統的字符串匹配算法中,當母串S與子串T在i和j點失配時,即
,此時i回溯到i - j + 1處(i = i - j + 1),j回溯到0 (j = 0)處繼續匹配,因而可知匹配算法效率不高,複雜度爲O(n * m)。java
而在KMP算法,當母串S與子串T在i和j點失配時,i不須要回溯,j只須要回溯到某一個特定位置便可總體複雜度爲O(n + m),。那j須要回溯到哪呢,咱們定義一個next數組,令next[j] = k表示當母串S和子串T在i點失配時,即, j須要回溯到k這個位置(k < j)。接下來咱們來討論如何肯定k點,並求出next數組。ios
若母串S和子串T在i和j點失配,,此時必有
,對於任意的
,有
。如有
,則有
,所以next[j] = k。算法
對於next[j + 1],若,則必有
,所以next[j + 1] = k + 1 = next[j] + 1。若
,顯然
,此時令母串
,子串
,此時就至關於母串
和子串
在j和k點失配,由next數組的定義,next[k] = k',即
,此時如有
,則
,所以next[j + 1] = k' + 1 = next[k] + 1,若
,此時須要重複上面的步驟去尋找更小的k''使得
,此時next[j + 1] = k'' + 1 = next[k'] + 1,若
,繼續尋找k''',直到到達next[0] = -1爲止。數組
C++代碼實現ui
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int max_size = 100000;
int Next[max_size] = {};
void make_next(char* t, int* next) {
int j = 0, k = -1;
next[j] = k;
while (j < strlen(t)) {
if (k == -1 || t[j] == t[k]) next[++j] = ++k; // next[j + 1] = k + 1;
else k = next[k]; // 尋找更小的k使得t[j] = t[k]
}
}
int kmp(char* s, char* t, int* next) {
int i = 0, j = 0;
make_next(t, next);
while (i < strlen(s)) {
if (j == -1 || s[i] == t[j]) i ++, j ++;
else j = next[j]; // i點失配,j回溯到next[j]點
if (j == strlen(t)) return i - j;
}
return -1;
}
int kmp_count(char* s, char* t, int* next) {
int i = 0, j = 0, v = 0;
make_next(t, next);
while (i < strlen(s)) {
if (j == -1 || s[i] == t[j]) i++, j++;
else j = next[j];
if (j == strlen(t)) v++, j = next[j];
}
return v;
}
int main() {
memset(Next, 0, sizeof(Next));
char* s = const_cast<char*>("abcabacabaa");
char* t = const_cast<char*>("aba");
int i = kmp(s, t, Next);
cout<<i<<endl;
memset(Next, 0, sizeof(Next));
int v = kmp_count(s, t, Next);
cout<<v<<endl;
}
複製代碼
JavaScript實現spa
function makeNext(t) {
let k = -1, next = [k], j = 0;
while (j < t.length) {
if (k === -1 || t[k] === t[j]) ++j, next.push(++k);
else k = next[k];
}
return next;
}
function kmp(s, t) {
let i = 0, j = 0, next = makeNext(t);
while (i < s.length) {
if (j === -1 || s[i] === t[j]) j++ , i++;
else j = next[j];
if (j === t.length) return i - j;
}
return -1;
}
function kmpCount(s, t) {
let i = 0, j = 0, v = 0, next = makeNext(t);
while (i < s.length) {
if (j === -1 || s[i] === t[j]) j++ , i++;
else j = next[j];
if (j === t.length) v++ , j = next[j];
}
return v;
}
let s = "ababababacadababa";
let t = "ba";
console.log(kmp(s, t));
console.log(kmpCount(s, t));
複製代碼