超级简单的序列自动机-处理子串匹配

接触序列自动机是因为训练的时候遇到的两个题目

牛客:https://ac.nowcoder.com/acm/contest/392/J

计蒜客:https://nanti.jisuanke.com/t/38232

序列自动机是在O(n)的复杂度内判断一个字符串是否是另一个字符串的子串(定义请自行百度

先上代码(懂原理的可以到此为止了

for(int i=n; i>0; i--)
{
	for(int j=1; j<=26; j++) nex[i-1][j-'a'] = nex[i][j-'a'];  //26是假定字符集为小写字母
	nex[i-1][s[i]-'a'] = i;
}

序列自动机就是开一个辅助空间nex[MAX_N][MAX_字符集],nex[i][j]表示的是在位置 i 的后面第一次出现 j 时的位置(贪心出现),在匹配子串时即可不断寻找子串的next元素最先在主串中出现的位置

我们就可以以递推的方式来存值了,多说无益,模拟才是王道

主串:abcda ,主串长度n = 5,下标从1的5,0的位置是虚设的,字符集 S = {a,b,c,d}

 nex[i][a]nex[i][b]nex[i][c]nex[i][d]
01234
15234
25034
35004
45000
50000

 

 

 

表格倒着看才对,在i==5的位置全0是初始的边界条件,接下来某个值为0就是在 i 位置后面没有 j 字符出现了

剩下的就是一个贪心的匹配过程,照着表格手动模拟一发即可

 

ps: nex[0][j]是很有用的,可以告诉我们元素j第一次出现在主串中的位置,所以输入的时候我们从s+1的位置开始输入,s[0]虚设成一个字符‘&’,不然的strlen(s)会返回0啊

串的匹配,都在代码里

char c; //读入子串
int f=0,p=0;  //p初始化为0,nex[0][c]表示c第一次出现的位置
while((c = getchar()) != '\n')
	if((p = nex[p][c-'a']) == 0) f = 1;  //递推找位置,p后面没有c就不能匹配成子串了
if(f) printf("NO\n");
else printf("YES\n");

我们假设主串长度lens,子串长度lenb,有n个子串,那么这个复杂度就是O(lens+n*lenb)

#include<stdio.h>
#include<string.h>
using namespace std;
typedef long long LL;
const int MAX_N = 1e5+50;
char s[MAX_N],c;
int nex[MAX_N][28],n,p,q;
int main(void)
{
	s[0] = '&';  //虚设一个用不到的字符
	scanf("%s",s+1);
	int len = strlen(s);
	for(int i=len; i>0; i--)
	{
		for(int j='a'; j<='z'; j++) nex[i-1][j-'a'] = nex[i][j-'a'];
		nex[i-1][s[i]-'a'] = i;
	}
	scanf("%d",&n);
	getchar();
	while(n--)
	{
		int f = 0,p=0;
		while((c = getchar()) != '\n')  //子串不需要存啊
			if((p = nex[p][c-'a']) == 0) f = 1;
		if(f) printf("NO\n");  //按需要修改咯
		else printf("YES\n");
	}
	return 0;
}

结束求波关注

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值