字符串模式匹配

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值