【算法】KMP - 字符串匹配算法
[by_041]
问题模板
- 给定两串字符串
str_1
、str_2
,判断串str_1
在串str_2
中出现了几次(记录每次出现的位置)
解法
对于str_1构建 nex[ ] 位移数组
-
利用模式串内部的前缀重复性,创建一个数组
nex[]
,nex[i]
表示str_1[0~nex[i]]
可以和str_1[(i-nex[i])~i]
匹配。 -
以
str_1="bbbabb"
为例,构造nex[]
的构造如下:
nex.clear();
nex.resize(str_1.size());
for(int i=0,j=nex[0]=-1;i<(int)str_1.size();nex[++i]=++j)
{
while(~j&&str_1[i]!=str_1[j])
j=nex[j];
}
下标 | 0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|---|
str_1[ ] | b | b | b | a | b | b |
nex[ ] | -1 | 0 | 1 | 2 | 0 | 1 |
利用 nex[ ] 将str_1与str_2进行模式匹配
-
设置
now_1
、now_2
,每次操作将str_1[now_1]
和str_2[now_2]
匹配。若成功,now变量双双加一;若失败,now_1=nex[now_1]
,特别的当nex[now_1]==-1
时,now_2++
。 -
以
str_1="bbbabb"
、str_2=bbbacbbbabbbabb
为例,扫描的过程如下:
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
str_2[ ] | b | b | b | a | c | b | b | b | a | b | b | b | a | b | b |
第1步 | 【b】 | b | b | a | b | b | |||||||||
第2步 | b | 【b】 | b | a | b | b | |||||||||
第3步 | b | b | 【b】 | a | b | b | |||||||||
第4步 | b | b | b | 【a】 | b | b | |||||||||
第5步 | b | b | b | a | 【b】 | b | (此 | 处 | nex | [4] | =0 | ) | |||
第6步 | 【b】 | b | b | a | b | b | (nex | [0] | =-1 | ) | |||||
第7步 | 【b】 | b | b | a | b | b | |||||||||
第8步 | b | 【b】 | b | a | b | b | |||||||||
第9步 | b | b | 【b】 | a | b | b | |||||||||
10 | b | b | b | 【a】 | b | b | |||||||||
11 | b | b | b | a | 【b】 | b | |||||||||
12 | b | b | b | a | b | 【b】 | ( | get | 6 | ) | |||||
13 | b | 【b】 | b | a | b | b | |||||||||
… | … | ( | get | 10 | ) | … |
洛谷模板题 - 模板代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
string str_1,str_2;
cin>>str_2>>str_1;
vector<int>nex;
nex.resize(str_1.size()+1);
for(int i=0,j=nex[0]=-1;i<(int)str_1.size();nex[++i]=++j)
{
while(~j&&str_1[i]!=str_1[j])
j=nex[j];
}
vector<int>anss;
for(int now_1=0,now_2=0;now_2<(int)str_2.size();now_2++)
{
while(now_1>0&&str_2[now_2]!=str_1[now_1])
now_1=nex[now_1];
if(str_2[now_2]==str_1[now_1])
now_1++;
if(now_1==(int)str_1.size())
{
anss.push_back(now_2-now_1+2);
now_1=nex[now_1];
}
}
for(auto i:anss)
cout<<i<<endl;
reverse(nex.begin(),nex.end());
nex.pop_back();
reverse(nex.begin(),nex.end());
for(auto i:nex)
cout<<i<<' ';
cout<<endl;
return 0;
}
代码模板
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
#define pattern_MAX_SIZE 1001
struct pattern //模式串类
{
int siz; //模式串长度
char str[pattern_MAX_SIZE]; //内容
int nex[pattern_MAX_SIZE]; //nex[]功能数组
vector<int>anss; //存放查询结果
void in() //输入内容
{
cin>>str;
size();
KMP_biuld_nex();
}
void out() //输出内容
{
puts(str);
}
int size() //更新siz
{
return siz=strlen(str);
}
void KMP_biuld_nex()//搭建nex[]功能数组
{
nex[0]=-1;
int i=1,j=0;
while(i<siz)
{
while(~j&&str[j]!=str[i])
j=nex[j];
nex[++i]=++j;
}
}
void KMP_match(char*astr)//模式串识别,将astr中每一次出现ptn(当前模式串)的首位置储存在成员变量anss里
{
anss.clear();
for(int now_str=0,now_ptn=0,size_str=strlen(astr),size_ptn=this->siz;now_str<size_str;now_str++)
{
while(now_ptn>0&&astr[now_str]!=this->str[now_ptn])
now_ptn=nex[now_ptn];
if(this->str[now_ptn]==astr[now_str])
now_ptn++;
if(now_ptn==size_ptn)
{
anss.push_back(now_str-now_ptn+1);
now_ptn=nex[now_ptn];
}
}
}
};
int main(void)
{
char a[1001];
// string a;
pattern b;
cin>>a;
b.in();
putchar('\n');
// //char a[1001];
b.KMP_match(a);
// // string a;
// b.KMP_match(const_cast<char*>(a.c_str()));
for(auto it:b.anss)
cout<<it<<endl;
putchar('\n');
return 0;
}