字符串匹配。给你两个字符串,寻找其中一个字符串是否包含另一个字符串,如果包含,返回包含的起始位置
1、 BF算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个字符和P的第二个字符;若不相等,则比较S的第二个字符和P的第一个字符,其实相当于将整个模式串往后移了一位,依次比较下去,直到得出最后的匹配结果。
2、KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n)。
在KMP算法中,为了确定在匹配不成功时,下次匹配时j的位置,引入了next[]数组,next[j]的值表示P[1...j-1]中最长的后缀等于相同字符序列的前缀的长度。
对于next[]数组的定义如下:
(1) next[j] = 0( j = 1)(t1与Si比较不相等时,下一步进行t1与Si+1的比较)
(2) next[j] = max(k)(1<k<j且 P[1...k-1]=P[j-k+1,j-1])
{设next[j]=k;(1<k<j)
若tk=tj 则next[j+1]=k+1;即next[j+1]=next[j]+1;
若tk!=tj 则next[j+1]=next[k]+1;
}
(3) next[j] = 1 (k=1)(不存在相同子串,下一步进行t1与si的比较)
因此KMP算法的思想就是:在匹配过程称,若发生不匹配的情况,则目标串的指针i不变,将模式串的指针j移动到next[j]的位置继续进行匹配;当指针j退至0时,指针i和指针j需同时增加1。也就是说,当发生不匹配的情况,让模式串继续往后移,而移动的位数就是模式串的长度-部分匹配值(最长相同前后缀的长度),因为BF里面每次指针i回溯,会导致符合匹配的字符再一次匹配,增大了时间开销。
求next数组是难点,一般有递推法(用一个while循环)和直接法两种。
模板1:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,k,len1,len2;
int next1[1000001];
char s1[1000001];
char s2[1000001];
inline void get_next() //求出next数组
{ //next数组是从 S[0到i-1]前子串 的前缀后缀最大值
int t1=0,t2;
next1[0]=t2=-1;
while(t1<len2)
if(t2==-1 || s2[t1]==s2[t2]) //类似于KMP的匹配
next1[++t1]=++t2;
else t2=next1[t2];//失配
}
inline void KMP() //KMP
{
int t1=0,t2=0;//从0位开始匹配
while(t1<len1) //临界值
{
if(t2==-1 || s1[t1]==s2[t2]) //匹配成功,继续
t1++,t2++;
else t2=next1[t2]; //失配
if(t2==len2) printf("第%d个位置成功匹配\n",t1-len2+1),t2=next1[t2];//t2==lenn2时,匹配成功;t1-len2+1即为第一个字母的位置
} //匹配成功后,t2置为next[t2]
}
int main(){
scanf("%s",s1);
scanf("%s",s2);
len1=strlen(s1);
len2=strlen(s2);
get_next();
KMP();
for(int i=1;i<=len2;++i)
printf("%d ",next1[i]);//输出next数组
printf("\n");
return 0;
}
模板2:
#include<bits/stdc++.h>
using namespace std;
int BF_Find(string& s,string& t)
{
int i=0,j=0,count=0;//初始化
while(i<s.size()) //未比较到串尾
{
if(s.at(i)==t.at(j)) //继续比较后续字符
{
i++;
j++;
count++;}
else //指针后退重新匹配
{
i=i-j+1;
j=0;
count=0;
}
if(count==t.size())
{
cout<<"BF算法模式匹配成功,起始位置是:"<<i-count+1<<endl;
return (i-count+1);
}
}
cout<<"字符串匹配失败!"<<endl;
return 0;
}
void GetNext(string& s,int *next)
{
int len = s.size();
next[0] = 0;
next[1] = 0;
int i = 1;
while(i < len - 1)
{
int j = next[i];
while(j > 0 && s.at(j) != s.at(i)) j = next[j];
if(s.at(j) == s.at(i)) next[i + 1] = j + 1;//根据next的定义推导出公式
else next[i + 1] = j;
i++;
}
}
void Getnextval(string & s,int *next)
{
int i=1;
next[1]=0;
int j=0;
int len=s.size();
while(i<len){
if(j==0||s.at(i)==s.at(j)){
++i;++j;
if (s.at(i)!=s.at(j)) next[i]=j;
else next[i]=next[j];
}
else j=next[j];
}
}
int KMP_Find(string& s,string&t)
{
int i=0,j=0;
int n =t.size();
int *next = new int[n];
GetNext(t,next);
while(i<s.size()&&j<t.size()) //两个串均未比较到串尾
{
if((j == 0)||(s.at(i)==t.at(j))) //继续比较后续字符
{
i++;
j++;
}
else
{
j = next[j];//模式串后移
}
} if(j==t.size())
{
int index=i-t.size()+1;
cout<<"KMP算法:子串从长串的第"<<index<<"位开始匹配成功!"<<endl;
return index;
}
cout<<"字符串匹配失败!"<<endl;
return 0;
}
int main()
{
string str1,str2;
cout<<"请输入主串:"<<endl;
cin>>str1;
cout<<"请输入子串:"<<endl;
cin>>str2;
KMP_Find(str1,str2);
BF_Find(str1,str2);
return 0;
}