【YbtOJ AC自动机 - 3】前缀匹配

本文介绍了如何使用AC自动机进行前缀匹配。通过构建AC自动机并处理母串,找出每个查询串与母串的最大匹配串长度。在解析过程中,标记特定路径表示的子串,最终解决查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前缀匹配


题目

在这里插入图片描述


输入

在这里插入图片描述

输出

输出有 MMM

每一行输出一个数,表示查询串的前缀与母串的最大匹配串长度、


输入样例

7 3
SNNSSNS
NNSS
NNN
WSEE

输出样例

4
2
0

解题思路

我们先将询问构成ACACAC自动机,然后让母串在上面跑,遍历到的点到rootrootroot形成的字符串一定是母串的子串

nxtnxtnxtrootrootroot形成的字符串也是母串的子串

那么我们对遍历到的点跳失配指针,并标记rootrootroot所形成的字符串就是母串的子串

最后再拿询问在上面跑,遇到标记的点就更新即可


程序如下

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>

using namespace std;

int n, m, tot;

char s[10000001], c[100001][101];

queue <int> q;

struct node
{
	int son[4], line;
	bool use;
}tree[1000001];

int t(char c)
{
	if(c == 'E') return 0;
	if(c == 'S') return 1;
	if(c == 'W') return 2;
	if(c == 'N') return 3;
	return -1;
}

void e(int o)
{
	int len = strlen(c[o]);
	int now = 0;
	for(int i = 0; i < len; ++i)
	{
		int tt = t(c[o][i]);
		if(!tree[now].son[tt]) tree[now].son[tt] = ++tot;
		now = tree[now].son[tt]; 
	}
}

void b()
{
	for(int i = 0; i <= 3; ++i)
	{
		if(tree[0].son[i])
		{
			q.push(tree[0].son[i]);
			tree[tree[0].son[i]].line = 0;
		}
	}
	while(!q.empty())
	{
		int now = q.front();
		q.pop();
		for(int i = 0; i <= 3; ++i)
		{
			if(tree[now].son[i]) 
			{
				q.push(tree[now].son[i]);
				tree[tree[now].son[i]].line = tree[tree[now].line].son[i];
			}
			else tree[now].son[i] = tree[tree[now].line].son[i];
		}
	}
}

void a()
{
	int now = 0;
	for(int i = 1; i <= n; ++i)
	{
		int tt = t(s[i]);
		int noww = tree[now].son[tt];
		while(noww)
		{
			tree[noww].use = 1;
			noww = tree[noww].line; 
		}
		now = tree[now].son[tt];
	}
}

int ask(int o)
{
	int len = strlen(c[o]);
	int now = 0, sum = 0;
	for(int i = 0; i < len; ++i)
	{
		int tt = t(c[o][i]);
		now = tree[now].son[tt];
		if(tree[now].use) sum = i + 1;
	}
	return sum;
}

int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++i)
	{
		s[i] = getchar();
		while(t(s[i]) == -1) s[i] = getchar();
	}
	for(int i = 1; i <= m; ++i)
	{
		scanf("%s",&c[i]);
		e(i);
	}
	b();
	a();
	for(int i = 1; i <= m; ++i)
		printf("%d\n",ask(i));
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值