一、模式匹配的概念
模式匹配是数据结构中字符串的一种基本运算,给定一个子串,要求在某个字符串中找出该字串相同的所有子串,这就是模式匹配。其中原字符串成为目标串,给定的子串为模式串。通俗理解如下图1-1:
二、常用的模式匹配算法
1、朴素的模式匹配算法(也称简单匹配算法,Brute-Force简称BF算法)
A.算法思想:
核心是穷举法。从目标串的的第一个字符起与模式串的第一个字符比较,若相等,则继续对字符进行后续的比较,否则目标串从第二个字符起与模式串的第一个字符重新比较,直至模式串中的每个字符依次和目标串中的一个连续的字符序列相等为止,此时称为匹配成功,否则匹配失败。
B.算法演示(给定目标串goodgoogle,模式串google)
C.代码实现
#include <iostream>
#include <string>
using namespace std;
int Match1(string str, string searchStr)
{
int i = 0;
int j = 0;
while (i < str.size() && j < searchStr.size())
{
if (str[i] == searchStr[j])
{
i++;
j++;
}
else//指针回退,重新开始匹配
{
i = i - j + 1;//目标串回到匹配位置的下一位置
j = 0;//模式串回到起始0位置
}
}
if (j >= searchStr.size())
{
return i - searchStr.size();//返回第一次匹配的首地址
}
else
{
return -1;
}
}
int main(){
string s = "goodgoogle";
string t = "google";
cout << Match1(s, t)<<endl;
}
D.算法分析
设目标串s的长度为n,模式串t的长度为m
最好情况下的时间复杂度O(m);
最差情况下的时间复杂度O(n*m);
平均时间复杂度O(n*m)。
2、KMP匹配算法
Knuth-Morris-Pratt算法(简称KMP),是由D.E.Knuth、J.H.Morris和V.R.Pratt共同提出的一个改进算法,消除了朴素的模式匹配算法中回溯问题,完成串的模式匹配。
A.算法思想:
设目标串为s,模式串为t, i、j 分别为指示s和t的指针,i、j的初值均为0。
若有 si = tj,则i和j分别增1;否则,i不变,j退回至j=next[j]的位置 ( 也可理解为串s不动,模式串t向右移动到si与tnext[j]对齐 );
比较si和tj。若相等则指针各增1;否则 j 再退回到下一个j=next[j]的位置(即模式串继续向右移动 ),再比较 si和tj。
依次类推,直到下列两种情况之一:
1)j退回到某个j=next[j]时有 si = tj,则指针各增1,继续匹配;
2)j退回至 j=-1,此时令指针各增l,即下一次比较 si+1和 t0
B.算法演示
C.代码实现
#include<iostream>
#include<string>
using namespace std;
//计算子串的next数组
void get_next(string t, int *next)
{
int i, j;
i = 1;
j = 0;
next[1] = 0;
while (i<t.size() - 1)
{
if (j == 0 || t[i] == t[j])//t[i]表示后缀的单个字符,t[j]表示前缀的单个字符
{
i++;
j++;
next[i] = j;
}
else
j = next[j]; //若字符不相同,则j值回溯
}
}
//返回子串t在主串t中第pos个字符之后的位置;若不存在,函数返回0
//s[0]和t[0]应该保存字符串的长度,这里我们随意保存一个字符#
int KMP(string s, string t) //默认从主串的第一个位置开始查找
{
int i = 0; //i用于表示主串s的当前位置
int j = 0; //j用于表示子串t的当前位置
int next[255]; //定义一个next数组
get_next(t, next); //对串做分析,得到next数组
while (i < s.size() && j < t.size()) //若i小于s的长度且j小于t的长度,循环继续
{
if (j == 0 || s[i] == t[j]) //两字符串相等,或j处于开始位,继续
{
i++;
j++;
}
else //j值退回合适的位置,i值不变
j = next[j];
}
if (j >= t.size())
return i - t.size();
else
return 0;
}
int main()
{
string s = "#goodgoogle";
string t = "#google";
cout << KMP(s, t) << endl;
return 0;
}
D.算法分析
对于长度为m的模式s和长度为n的目标t的模式匹配,KMP算法的时间复杂度为O(m+n)。