KMP算法
计算 n e x t next next 数组
void getnext(){
nextval[0] = -1;
int j = -1, i =0;
while(i < t.length()){
if(j == -1 || t[i] == t[j]){++i; ++j; nextval[i] = j;}
else j = nextval[j];
}
}
优化后的 n e x t next next 数组
void getnext(){
nextval[0] = -1;
int j = -1, i = 0;
while(i < t.length()){
if(j == -1 || t[i] == t[j]){
++i; ++j;
if(t[i] != t[j]) nextval[i] = j;
else nextval[i] = nextval[j];
}
else j = nextval[j];
}
}
应用一:查找是否存在
#include<iostream>
#include<string>
using namespace std;
typedef long long ll;
const int maxn = 10000 + 10;
int nextval[maxn];
string s, t;
void getnext(){
nextval[0] = -1;
int j = -1, i = 0;
while(i < t.length()){
if(j == -1 || t[i] == t[j]){
++i; ++j;
if(t[i] != t[j]) nextval[i] = j;
else nextval[i] = nextval[j];
}
else j = nextval[j];
}
}
int kmp(){
getnext();
int i = 0, j = 0;
while(i < s.length()){
if(j == -1 || s[i] == t[j]) {++i; ++j;}
else j = nextval[j];
if(j == t.length()) {return i - j;} //返回第一个字符的下标,如果要求第几个,则+1
}
return -1; //没有匹配成功
}
int main()
{
cin>>s>>t;
int res = kmp();
if(res == -1){
cout<<"不存在"<<endl;
}
else {
cout<<"出现在第"<<res+1<<"个位置"<<endl;
}
return 0;
}
应用二:统计匹配次数
注意是否可重叠,在代码中有体现
题目描述:统计匹配次数(不重叠)
题目来源:hdu2087
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 10;
int nextval[maxn];
int num[maxn];
int cnt = 0;
string s, t;
void getnext(){
nextval[0] = -1;
int j = -1, i = 0;
while(i < t.length()){
if(j == -1 || t[i] == t[j]) {
++i; ++j;
if(t[i] != t[j]) nextval[i] = j;
else nextval[i] = nextval[j];
//nextval[i] = j;
}
else j = nextval[j];
}
}
int kmp(){
getnext();
int i = 0, j = 0;
int res = 0;
while(i < s.length()){
if(j == -1 || s[i] == t[j]) {++i; ++j;}
else j = nextval[j];
if(j == t.length()) {
res++;
j=0; //不能重叠
//j = nextval[j] 可重叠
//例如aaaaaa与aa进行匹配,不能重叠就是3,可重叠就是5
}
}
return res;
}
int main()
{
while(cin>>s,s!="#"){
cin>>t;
cout<<kmp()<<endl;
}
return 0;
}
序列自动机
匹配字符串,可以不连续
题目来源:牛客小白月赛12J
#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = 1e6 + 10;
char s[maxn];
int n,q,lst[26]; //lst[i]=k 表示字符i第一次出现的位置为k
int son[maxn][26]; //son[i][j]=k 表示s串中第i个位置上的字符的下一个字符为j的位置为k
int main()
{
scanf("%s",s+1);
n = strlen(s + 1);
for (int i = 0; i < 26; i++) //初始化
son[n + 1][i] = lst[i] = n + 1;
for (int i = n; ~i; i--){ //~i 表示 i>=0
for (int j = 0; j < 26; j++)
son[i][j] = lst[j];
lst[s[i] - 'a'] = i;
}
cin >> q;
while (q--){
scanf("%s",s+1);
int x = 0;
for (int i = 1; s[i]; i++)
x = son[x][s[i] - 'a'];
if (x == n + 1) cout << "No" << endl;
else cout << "Yes" << endl;
}
return 0;
}