KMP算法
- 用于改进字符串匹配算法
字符串匹配
- 主串中是否存在模式串
模式匹配
- 基本思想
- 从模式串的第一个字符和主串的第一个字符比较
- 不同时又从模式串的第一个字符和主串的第二个字符比较
- 直到匹配成功或匹配不成功
- 总结:太慢
KMP
- 基本思想
- 在模式匹配中若\(S[i]==P[j]\)
- 若\(S[i]!=P[j]\)
- 将模式串右移至\(nx[j]\)
- 即比较\(S[i],P[nx[j]]\)
- 重复上述过程至\(j==m,i==n\)结束
- \(nx[]\)数组表示在一次匹配不成功的情况最多可在模式串中向前跳几个字符
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
const int Max=100010;
int nx[Max];
//前缀数组(或next[]),
//表示在这一次匹配不成功的情况最多可在模式串中向前跳几个字符
//也描述了模式串的对称程度
string S;//主串
string P;//模式串
int T,N,M,ans;
void makenx(int M)//模式串
{//计算前缀数组
int i=0,j=-1;
//i表示当前计算的是第几个(即前缀数组的下标)
//j表示能跳到第几个(即前缀数组的值)
nx[i]=j;
//在第0个字符就匹配不成功,nx[0]=-1,
//也就是说在第0个字符就匹配不成功时,匹配主串的下一个字符
while(i<M)//计算完时跳出循环
if(j==-1||P[i]==P[j])
//j==-1时,表示没有字符和第i个字符相同
//p[i]==p[j]表示当前字符(i)和字符(j)匹配
i++,j++,nx[i]=j;
//以上两种情况都要记录nx[]值即nx[i]=j
//注意这里是先加后记录,
//如果s[]!=p[i+1],因为P[i]==P[j],可以比较S[]与P[j+1],即nx[i+1]=j+1;
//j==-1时,此时如果S[]!=P[i+1]时,因nx[i+1]=j+1=0,即重新匹配S[0]
else
j=nx[j];
//当不满足以上两种情况时,即j!=-1,且P[i]!=P[j]
//应该向前找是否相同
}
int Kmp(int N,int M)//KMP匹配算法
{
int i=0,j=0,ans=0;
//i表示主串下标
//j表示当前匹配到模式串的哪一位
//ans表示有几个重复子串
while((i<N)&&(j<M))
//比较完时跳出循环
//i跳出循环表示主串循环完
//j跳出循环表示模式串匹配完
{
if(j==-1||S[i]==P[j]) i++,j++;
//重新匹配或匹配成功时i++,j++
else j=nx[j];//不成功时向前跳进行重新匹配
// if(j==M) ans++,j=nx[M-1],i--;用于计算几个重复子串(模式串)
}//
if(i>=M) return i-M;//下标运算
else return -1;//匹配失败
// return ans;
}
int main()
{
scanf("%d",&T);//次数
while(T--)
{
cin>>S>>P;//输入
N=S.size();M=P.size();
makenx(M);ans=Kmp(N,M);
cout<<ans<<endl;//输出
}
return 0;
}