字符串匹配最基础的算法是一个字符一个字符的匹配,若当前不匹配则指针指向下一个字符。基础算法的写法为:
int findString(string &s, string &t){
int slen = s.size();
int tlen = t.size();
if (slen < tlen) {
return -1;
}
int j = 0;
for (int i = 0;i < slen;i++) {
int tmp = i;
for (j = 0;j < tlen;j++) {
if (s[tmp] == t[j]) {
tmp++;
}
else {
break;
}
}
if (j == tlen) {
return i;
}
}
return -1;
}
还有一种Rabin-Karp算法这种算法的基础思想是把字符串转为数字,这是一种很奇妙的算法,但是我不是很喜欢,因为对于ABC这种如果我们把他转为数字A对应1,B对应2,…K对应11。
那么111可以为AAA,也可以为AK,也可以为KA。参考题目https://leetcode.com/problems/decode-ways/#/description
因此在此不做叙述。
KMP算法是一种比较经典的算法,其重点在于匹配表的建立。匹配表是指对字符串p,p0p1…pk = pj - kpj - k + 1…pj则p[j + 1] = k.这么设计的原因是:
s0 s1 s2 …sk…sj-k sj-k+1 …sj sj+1…sn
p0p1…pk…pj-k pj-k+1…pj pj+1…pm
如果pj+1 != sj+1那么,p可以向前移动k个单位保证s[j-k…j] = p[0…k]
这样减少了一部分不必要的匹配。
KMP算法的代码如下:
void createPrefix(string &s, vector<int> &nexts) {
if (s.size() != nexts.size()) {
return;
}
nexts[0] = -1;
int k = -1, j = 0;
while (j < s.size() - 1) {
if (k == -1 || s[j] == s[k]) {
k++;
j++;
//这里很关键p[j] != p[next[j]]
if (nexts[j] != nexts[k]) {
nexts[j] = k;
}
else {
nexts[j] = nexts[k];
}
}
else {
k = nexts[k];
}
}
}
int findStringKMP(string &s, string &t) {
vector<int> tPre(t.size(), 0);
createPrefix(t, tPre);
int i = 0, j = 0;
int slen = s.size(), tlen = t.size();
while (i < slen && j < tlen) {
if (j == -1 || s[i] == t[j]) {
i++;
j++;
}
else {
j = tPre[j];
}
}
if (j == tlen) {
return i - j;
}
return -1;
}