算法第四版- 5.3子字符串查找

本文详细解读了KMP算法、Boyer-Moore(BM)算法、Rabin-Karp(RK)算法和Sunday算法在子字符串查找中的应用,通过实例和优化分析,展示了这些算法在提高搜索效率上的特点。从next数组到哈希映射,带你领略字符串匹配的多种策略。
摘要由CSDN通过智能技术生成

算法第四版- 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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值