【算法】字符串匹配


1.经典的KMP算法

  • 时间复杂度O(n+m):其中n为文本串s的长度,m为模式串p的长度。因为首先要遍历模式串求解部分匹配数组next,然后遍历文本串寻找匹配起始字符的下标。

  • 空间复杂度为O(m):其中m为模式串的长度,用来存放next数组。

// kmp参考代码
//      p: a  b c d a b d a
// next: -1 0 0 0 0 1 2 0
// 可以直观理解为先求出最长前缀后缀公共长度,然后右移一位得到next的结果。
void get_next(std::string p, int *next)
{
	int plen = p.size();

	int i = 0;
	int j = -1;
	next[0] = -1;
	while (i next(plen, 0);
	get_next(p, next);
	int i = 0;
	int j = 0;
	while (i

2.效率更高的Sunday算法

  • 时间复杂度O(n):其中n为文本串s的长度。因为只需要遍历文本串一遍,跳过的间隔相比kmp更大。

  • 空间复杂度O(1):只用到有限的几个指示变量。

// sunday
// 寻找next所指向的字符在模式串的最右侧出现的位置,然后更新next的值
void helper(std::string p, int plen, char ch, int *next)
{
	int pos = plen-1;
	for (int i=plen-1; i>=0; i--)
	{
		if (p[i]==ch) {
			pos = i;
			break;
		}
	}
	// 模式串中不包含ch字符,则next向后再移动一位
	if (pos==plen-1 && ch!=p[pos])
	{
		(*next)++;
	} else {
		// 如果找到了字符所在位置,则更新next的值
		*next -= pos;
	}
}

// aaabcdabaaabcdabdamns
// abcdabda
int sunday(std::string s, std::string p)
{
	if (s.empty()) return -1;
	if (p.empty()) return 0;

	int slen = s.size();
	int plen = p.size();

	int i = 0;
	int j = 0;
	int next = 0;
	while (i