牛客 每日一题 7 月月查华华的手机 题解(枚举优化)

题目链接

题目大意

给一个文本串和一个n,接着n个模板串,问文本串中是否有子序列和模板串相等

题目思路

前言

emmm,我只会暴力,暴力的话是O(∣A∣∑∣Bi∣)。后面我觉得应该是kmp或者hash等等,没想到居然是优化枚举。确实学算法不应该自己吓自己。并不是什么高深的算法

正文

我们可以观察,比如主串(华华的昵称)是abcdefgh,子串(小姐姐的昵称)是 aez,那么当 a 匹配上了之后,再去枚举主串中的 bcd 其实是没有意义的,如果我们能做到在 a 之后跳到 a 后面第一个 e 然后在 e 之后跳到后面的第一个 z(如果有的话),直到子串遍历完或者跳不动了,就能出结果了。(如果主串中有两个一样的字母,我们肯定会选前一个,因为是子序列,如果选前一个不可以,那么选后面的同一个字母肯定就更不可以了,因为选越靠后的字母给后面留下的选择余地就越小。)

那么我们怎么实现在主串跳着枚举这个事情呢?
开一个数组next[i] [‘a’…‘z’]表示主串第 i 个字母之后的第一个’a’…‘z’ 分别在哪一个位置,子串每次与之匹配的时候顺着往后走就行。
next数组的维护只需要从后往前扫描主串,在 扫描到第 i 位的时候始终维护一个数组 last[‘a’…‘z’] 表示当前位置往后最靠前的字母’a’…‘z’
分别在哪,然后把这个数组复制给next[i]就可以了。
这样的话每次匹配的复杂度是O(|Bi|)的(只要顺着Bi遍历完就可以判断是不是子序列了)。总复杂度是O(26∣A∣+∑∣Bi∣)

代码

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e6+5;
char a[maxn],b[maxn];
int t,last[30],nxt[maxn][30];
void init(){
	memset(last,-1,sizeof(last));//-1表示不存在 
	int d=strlen(a+1);
	for(int i=d;i>=1;i--){//注意要逆向枚举 
	//nxt[i][j]表示i位置后j的位置 
		for(int j=1;j<=26;j++){
			nxt[i][j]=last[j];
			//将last['a'...'z']复制给当前的next[i]['a'...'z']
		}
		last[a[i]-'a'+1]=i;//当前字母修改last
	}
}
bool check(){
	int da=strlen(a+1);
	int db=strlen(b+1);
	int pos=last[b[1]-'a'+1];
	if(pos==-1){//没有第一个元素 
		return 0;
	}
	for(int i=2;i<=db;i++){
		pos=nxt[pos][b[i]-'a'+1];
		if(pos==-1){
			return 0;
		}
	}
	return 1;
}
int main(){
	scanf("%s",a+1);
	init();
	scanf("%d",&t);
	while(t--){
		scanf("%s",b+1);
		if(check()){
			printf("Yes\n");
		}else{
			printf("No\n");
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值