算法第四版- 5.3子字符串查找
1.Kmp算法
先把课本的翻译一下。用了一个二维数组。
算了,懒得看了,放着待着吧。(话说我为啥要写这个呢…)
#include <iostream>
#include <vector>
using namespace std;
int R = 0, m = 0;
vector<vector<int>>dfa;
void KMP(string pat)
{
R = 256;
m = pat.length();
dfa.resize(R, vector<int>(m));
dfa[pat.at(0)][0] = 1;
for (int x = 0, j = 1; j < m; j++) {
for (int c = 0; c < R; c++)
dfa[c][j] = dfa[c][x];
dfa[pat.at(j)][j] = j + 1;
x = dfa[pat.at(j)][x];
}
}
int search(string txt)
{
int n = txt.length();
int i, j;
for (i = 0, j = 0; i < n && j < m; i++) {
j = dfa[txt.at(i)][j];
}
if (j == m) return i - m;
return n;
}
int main()
{
string a1 = "aabbaa";
string p1 = "ba";
KMP(p1);
cout << search(a1);
return 0;
}
然后说一下国内的版本,国内通常是以next数组,即一个一维数组。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
void GetNext(char* p, int next[])
{
int pLen = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
//k==-1的意思,在一开始,前面几个数都是k=next[k]=-1,从而使j不停++
if (k == -1 || p[j] == p[k])
{
++k;
++j;
next[j] = k;
}
else
{
k = next[k]; //求next的过程又调用了next数组
}
}
}
//优化过后的next 数组求法
void GetNextval(char* p, int next[])
{
int pLen = strlen(p);
next[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k])
{
++j;
++k;
//较之前next数组求法,改动在下面4行
if (p[j] != p[k])
next[j] = k; //之前只有这一行
else
//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
next[j] = next[k];
}
else
{
k = next[k];
}
}
}
int KmpSearch(char* s, char* p)
{
int i = 0;
int j = 0;
int sLen = strlen(s);
int pLen = strlen(p);
int* next = new int[pLen];
GetNext(p, next);
while (i < sLen && j < pLen)
{
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
if (j == -1 || s[i] == p[j])
{
i++;
j++;
}
else
{
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = next[j];
}
}
if (j == pLen)
return i - j;
else
return -1;
}
建议以"abcabdddabcabc"为例子去求一遍next数组,也能体会到为啥要优化,来省略一点嵌套。
不过知乎和CSDN写的关于KMP的文章都好长,要不就是好难懂,泪目。而且next[0]为0还是-1,也不统一。
2.BM算法
首先给出课本的翻译
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int R;
string pat;
vector<int> right1;
void BoyerMoore(string pat1)
{
R = 256;
pat = pat1;
for (int c = 0; c < R; c++)
right1[c] = -1;
for (int j = 0; j < pat.length(); j++)
right1[pat.at(j)] = j;
}
// 相当于哈希映射 字母->下标
int search(string txt)
{
int m = pat.length();
int n = txt.length();
int skip;
for (int i = 0; i <= n - m; i += skip) { //i对应的是主字符串txt
skip = 0;
for (int j = m - 1; j >= 0; j--) { // j对应的是短的pattern串
if (pat.at(j) != txt.at(i + j)) {
skip = max(1, j - right1[txt.at(i + j)]);
break;
}
}
if (skip == 0) return i;
}
return n;
}
然后BM的分析,看这个blog
https://blog.csdn.net/v_JULY_v/article/details/7041827?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163403608316780271564227%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=163403608316780271564227&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-2-7041827.pc_search_result_cache&utm_term=KMP&spm=1018.2226.3001.4187
3.RK算法
了解一下,用到了随机数,不写了。
https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/RabinKarp.java.html
4.Sunday算法
lc第28题
自从学会了Sunday,再也不想看前面的KMP了。
虽然看了一晚上KMP,已经忘记Sunday咋写了。。
class Solution {
public:
int strStr(string haystack, string needle) {
// 建立偏移表
int hSize=haystack.size();
int nSize=needle.size();
unordered_map<char, int> pianyi;
for(int i=0;i<nSize;i++) pianyi[needle[i]]=nSize-i;
// 遍历
int i=0;
while(i<=hSize-nSize){
if(haystack.substr(i,nSize)==needle) return i;
else{
// 查询substr后的字符的偏移值
if(i+nSize>hSize-1) return -1;
else{
if(pianyi.find(haystack[i+nSize])!=pianyi.end()){
i+=pianyi[haystack[i+nSize]];
}
else{
i+=nSize+1;
}
}
}
}
return -1;
}
};