KMP字符串匹配
前言
KMP算法是一种字符串匹配算法,可用于在一个文本串S内查找一个模式串P的出现位置。
KMP算法的思想是,当出现不匹配时,可以利用已经匹配的部分信息,将模式串向右移动尽可能小的距离,从而继续匹配。这个距离是由模式串的前缀和后缀的最长公共子串长度决定的。
KMP算法的核心是计算出模式串P的前缀和后缀的最长公共子串长度数组next[],这个数组可以预处理出来,并且在匹配时使用。
KMP算法的时间复杂度为O(n+m),其中n是文本串的长度,m是模式串的长度。
KMP算法的优点是在匹配过程中不会回退文本串指针,所以效率比暴力匹配要高。
1.字符串匹配的暴力做法
int ViolentMatch(char* s, char* p)
{
int sLen = strlen(s);
int pLen = strlen(p);
int i = 0;
int j = 0;
while (i < sLen && j < pLen)
{
if (s[i] == p[j])
{
//如果当前字符匹配成功(即S[i] == P[j]),则i++,j++
i++;
j++;
}
else
{
//如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0
i = i - j + 1;
j = 0;
}
}
//匹配成功,返回模式串p在文本串s中的位置,否则返回-1
if (j == pLen)
return i - j;
else
return -1;
}
该算法的核心思想是从文本串的第一个字符开始,逐个比较与模式串中的字符是否相同,直到匹配成功或失败。其具体实现如下:
假设文本串为s,模式串为p,分别获取它们的长度sLen和pLen。
初始化i和j两个变量,分别表示文本串s和模式串p的指针位置,初始化为0。
在while循环中,逐个比较文本串s和模式串p中的字符是否相同,直到匹配成功或者比较完所有字符。如果当前字符匹配成功,即S[i] == P[j],则i和j分别加1;否则,i回退到当前模式串与文本串对齐的位置(i - (j - 1)),j重置为0,重新从模式串开头开始匹配。
最后,如果匹配成功,则返回模式串p在文本串s中的位置,即i-j;否则,返回-1表示匹配失败。
虽然该算法思路简单,但是它的时间复杂度为O(n*m),其中n和m分别为文本串和模式串的长度。当文本串和模式串的长度较大时,算法的效率会非常低下。因此,该算法在实际应用中很少被使用。
2. KMP算法的实现
KMP算法的实现包括两个部分:计算模式串的Next数组和匹配过程。
问题举例
给定一个字符串 S,以及一个模式串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。模式串 P在字符串 S中多次作为子串出现。求出模式串 P字符串 S中所有出现的位置的起始下标。
输入格式
第一行输入整数 N,表示字符串 P 的长度。
第二行输入字符串 P
第三行输入整数 M,表示字符串 S 的长度。
第四行输入字符串 S
输出格式
共一行,输出所有出现位置的起始下标(下标从 0开始计数),整数之间用空格隔开。
输入样例:
3
aba
5
ababa
输出样例:
0 2
ac代码如下
#include<iostream>
using namespace std;
const int N=100010,M=1000010;
char q[N],s[M];
int ne[N];
int main()
{
int n,m;
cin>>n>>q+1>>m>>s+1;///让两个直接从1开始
for(int i=2,j=0;i<=n;i++)
{
while(j&&q[i]!=q[j+1]) j=ne[j];
if(q[i]==q[j+1]) j++;
ne[i]=j;
}
for(int i=1,j=0;i<=m;i++)
{
while(j&&s[i]!=q[j+1]) j=ne[j];
if(s[i]==q[j+1]) j++;
if(j==n)
{
printf("%d ",i-j);
j=ne[j];
}
}
return 0;
}
2.1 计算模式串的Next数组
模式串的Next数组是一个重要的辅助数组,用于在匹配时确定模式串向右移动的距离。Next数组的计算方法如下:
for (int i = 2, j = 0; i <= m; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
其中,i表示当前计算到的位置,j表示当前位置的最长前缀和后缀的最长公共子串长度。算法的核心是不断利用已经计算出的前缀和后缀的最长公共子串长度,更新j的值。
2.2匹配过程
匹配过程是利用已经计算出的Next数组,在文本串中查找模式串的过程。具体实现如下:
for (int i = 1, j = 0; i <= n; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == m)
{
j = ne[j];
// 匹配成功后的逻辑
}
}
其中,i表示当前匹配到的文本串的位置,j表示当前匹配到的模式串的位置。如果当前位置不匹配,则根据Next数组将模式串向右移动尽可能小的距离,继续匹配。如果匹配成功,即j等于模式串长度m,说明找到了一个匹配,将j赋值为Next数组中对应位置的值,继续匹配下一个位置。
总结
总的来说,KMP算法是一种高效的字符串匹配算法。它利用已经匹配的部分信息,尽可能小的移动模式串,从而继续匹配。KMP算法的实现包括两个部分:计算模式串的Next数组和匹配过程。Next数组是一个重要的辅助数组,用于在匹配时确定模式串向右移动的距离。匹配过程是利用已经计算出的Next数组,在文本串中查找模式串的过程。KMP算法的时间复杂度为O(n+m),其中n是文本串的长度,m是模式串的长度。
在实际应用中,KMP算法常用于字符串匹配问题,如查找某个字符串是否出现在另一个字符串中,计算两个字符串的最长公共前缀等。掌握KMP算法可以帮助我们更好地解决这些问题,提高程序的效率。
看完这篇文章如果没法理解的,可以尝试跟着代码画图理解。画图可以刚直观的了解代码的运行和算法的原理。