目录
我学到的是朴素模式匹配算法和KMP算法,这些算法对我而言思路都挺绕的,所以记录一下
朴素模式匹配算法
思路
1.遍历整个主串,找到所有子串
2.用两个指针i和j分别遍历子串和模式串中的每一个元素,如果两个元素相等,则继续遍历,如果两个元素不相等,说明当前子串和模式串不匹配,则主串的工作指针应当指向下一个子串的首地址(i-j+2),而模式串的工作指针应当指向1
代码实现
int Index(SString S,SString T)
{
int i=1;
while(i<=S.length-T.length+1) //不用遍历所有的数,当遍历到最后子串长度小于模式串长度时,肯定不匹配
{
SString Sub;
SubString(Sub,S,i,T.length);
if(StrCompare(Sub,T)==0) return i;
else i++;
}
return 0;
}
完整代码实现
#include<bits/stdc++.h>
#define MaxSize 20
using namespace std;
string str1,str2;
typedef struct
{
char data[MaxSize];
int length;
}SString;
void InitString(SString S)
{
for(int i=0;i<MaxSize;i++)
{
S.data[i]=0;
}
S.length=0;
}
int Index(SString S,SString T)
{
int i=1,j=1;
while(i<=S.length&&j<=T.length)
{
if(S.data[i]==T.data[j])
{
i++;
j++;
}
else
{
i=i-j+2; //i也要返回到下一个要遍历的子串的首地址
j=1; //j要返回到第1个位置
}
}
if(j>T.length) return i-T.length;
else return 0;
}
int main()
{
//freopen("A.txt","r",stdin);
cin>>str1>>str2;
SString S,T;
InitString(S);
InitString(T);
//赋值操作
for(int i=1;i<=str1.size();i++)
{
S.data[i]=str1[i-1]; //保证SString的静态数组的第0个位置没有元素
}
S.length=str1.size();
for(int i=1;i<=str2.size();i++)
{
T.data[i]=str2[i-1];
}
T.length=str2.size();
//打印
for(int i=1;i<=S.length;i++)
{
cout<<S.data[i]<<" ";
}
cout<<endl;
for(int i=1;i<=T.length;i++)
{
cout<<T.data[i]<<" ";
}
cout<<endl;
int k=Index(S,T);
cout<<k<<endl;
return 0;
}
总结
这个算法会遍历字符串中的所有子串,最坏情况下时间复杂度为O(mn)
因此为了提高算法执行效率,我们需要为模式串引入next[]数组,在上面的算法中,工作指针i和j每当遇到不匹配都要进行回溯,而next[]数组可以使得遇到不匹配时只有模式串的工作指针需要回溯,这样就可以减少很多不必要的比较,提高效率
KMP算法
主要思路是让主串指针不回溯
思路
1.求next[]数组
2.遍历整个主串,找到所有子串
3.用两个指针i和j分别遍历子串和模式串中的每一个元素,如果两个元素相等,则继续遍历,如果两个元素不相等,说明当前子串和模式串不匹配,则将模式串的工作指针j指向next[j]
求next[j]数组(手算)
next[j]的含义:在模式串遍历到第j个元素的时候发生匹配失败,应当将j回溯到next[j]的位置
next[j]=模式串中j之前的字符串的最长相等前后缀的长度+1
特别的,为了简化代码,固定next[1]=0
结论:next[2]=1,因为第二个元素之前只有一个元素,一个元素不存在前缀和后缀(因为前缀和后缀不包括自身)
代码实现
#include<bits/stdc++.h>
#define MaxSize 20
using namespace std;
string str1,str2;
int next[MaxSize];
typedef struct
{
char data[MaxSize];
int length;
}SString;
void InitString(SString S)
{
for(int i=0;i<MaxSize;i++)
{
S.data[i]=0;
}
S.length=0;
}
void get_next(SString T,int next[])
{
int i=1,j=0;
next[1]=0;
while(i<T.length)
{
if(j==0||T.data[i]==T.data[j])
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}
}
int Index_KMP(SString S,SString T)
{
int i=1,j=1;
int next[T.length+1];
get_next(T,next); //next是引用类型
while(i<=S.length&&j<=T.length)
{
if(j==0||S.data[i]==T.data[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j>T.length) return i-T.length;
else return 0;
}
int main()
{
//freopen("A.txt","r",stdin);
cin>>str1>>str2;
SString S,T;
InitString(S);
InitString(T);
//赋值操作
for(int i=1;i<=str1.size();i++)
{
S.data[i]=str1[i-1]; //保证SString的静态数组的第0个位置没有元素
}
S.length=str1.size();
for(int i=1;i<=str2.size();i++)
{
T.data[i]=str2[i-1];
}
T.length=str2.size();
//打印
for(int i=1;i<=S.length;i++)
{
cout<<S.data[i]<<" ";
}
cout<<endl;
for(int i=1;i<=T.length;i++)
{
cout<<T.data[i]<<" ";
}
cout<<endl;
int k=Index_KMP(S,T);
cout<<k<<endl;
return 0;
}
总结
KMP算法的时间复杂度:O(n+m)
如果模式匹配过程中模式串没有被大量地部分匹配,则KMP算法和朴素模式匹配算法的效率差不多,只有模式串被大量地部分匹配时,KMP算法的优势才凸显
进一步优化KMP算法
求nextval[]数组
1.如果当前元素j和next[j]指向的元素相同,则nextval[j]=nextval[naxt[j]]
2.如果不相等,则nextval[j]=next[j]
代码实现
void get_nextval(SString T,int nextval[])
{
nextval[1]=0;
int next[T.length+1];
get_next(T,next);
for(int i=2;i<=T.length;i++)
{
if(T.data[i]==T.data[next[i]])
{
nextval[i]=nextval[next[i]];
}
else
{
nextval[i]=next[i];
}
}
for(int i=1;i<=T.length;i++)
{
cout<<nextval[i]<<" ";
}
cout<<endl;
}
完整代码实现
#include<bits/stdc++.h>
#define MaxSize 20
using namespace std;
string str1,str2;
int next[MaxSize];
int nextval[MaxSize];
typedef struct
{
char data[MaxSize];
int length;
}SString;
void InitString(SString S)
{
for(int i=0;i<MaxSize;i++)
{
S.data[i]=0;
}
S.length=0;
}
void get_next(SString T,int next[])
{
int i=1,j=0;
next[1]=0;
while(i<T.length)
{
if(j==0||T.data[i]==T.data[j])
{
i++;
j++;
next[i]=j;
}
else
{
j=next[j];
}
}
for(int i=1;i<=T.length;i++)
{
cout<<next[i]<<" ";
}
cout<<endl;
}
void get_nextval(SString T,int nextval[])
{
nextval[1]=0;
int next[T.length+1];
get_next(T,next);
for(int i=2;i<=T.length;i++)
{
if(T.data[i]==T.data[next[i]])
{
nextval[i]=nextval[next[i]];
}
else
{
nextval[i]=next[i];
}
}
for(int i=1;i<=T.length;i++)
{
cout<<nextval[i]<<" ";
}
cout<<endl;
}
int Index_KMP(SString S,SString T)
{
int i=1,j=1;
int nextval[T.length+1];
get_nextval(T,nextval);
while(i<=S.length&&j<=T.length)
{
if(j==0||S.data[i]==T.data[j])
{
i++;
j++;
}
else
{
j=nextval[j];
}
}
if(j>T.length) return i-T.length;
else return 0;
}
int main()
{
//freopen("A.txt","r",stdin);
cin>>str1>>str2;
SString S,T;
InitString(S);
InitString(T);
//赋值操作
for(int i=1;i<=str1.size();i++)
{
S.data[i]=str1[i-1]; //保证SString的静态数组的第0个位置没有元素
}
S.length=str1.size();
for(int i=1;i<=str2.size();i++)
{
T.data[i]=str2[i-1];
}
T.length=str2.size();
//打印
for(int i=1;i<=S.length;i++)
{
cout<<S.data[i]<<" ";
}
cout<<endl;
for(int i=1;i<=T.length;i++)
{
cout<<T.data[i]<<" ";
}
cout<<endl;
int k=Index_KMP(S,T);
cout<<k<<endl;
return 0;
}