链接:https://ac.nowcoder.com/acm/contest/1083/B
来源:牛客网
题目描述
给出一个长度为n的字符串s和q个查询。对于每一个查询,会输入一个字符串t,你需要判断这个字符串t是不是s的子串。子串的定义就是存在任意下标a<b<c<d<e,那么”s[a]s[b]s[c]s[d]s[e]”就构成s的一个子串。如”abc”的子串有”a”、”b”、”c”、”ab”、”ac”、”bc”、”abc”。
输入描述:
第一行两个数n,q。1<=n,q<=1e5。
第二行一个长度为n的字符串s,所有字符都为小写拉丁字符。
接下来q行每行一个字符串t。1<=|t|<=50。
输出描述
对于每个查询,如果t是s的字串,输出”YES”,否则输出”NO”。每个答案占一行。
输入
8 4
ababcbaa
abac
accb
aaaa
abcba
输出
YES
NO
YES
YES
题解
这题暴力 O(q*n *t) 没得做,因为是子串,并不连续,所以ac自动机没得做,最终弄到一个序列自动机的模板。
根据子序列在母序列中可能不连续的性质,我们只需要能快速的查到当前字符第一次在母串的位置,不断跳着查询,就能将复杂度缩成 O( min(q*t,n*26 ) )
用空间换时间,用一个二维数组 nxt[i][j],记录母串中第i个字符后面,26个字符第一次出现的位置,因为需要计算第 i 个字符后面字符,所以需要从后往前更新nxt[i][j],同时用数组now[i]保存当前位置后面的字符第一次出现的位置,每次往前移动,只需要将now赋值给nxt[i]即可。
预处理完,扫一遍子串查询即可
#include<bits/stdc++.h>
using namespace std;
#define maxn 100009
#define ll long long
#define inf 1000000009000000000
#define IOS ios::sync_with_stdio(false)
int nxt[maxn][27],now[27];
//nxt记录母串第i个字符后面,26个字符第一次出现的位置
//now记录26个字符第一次出现的位置
int main()
{
IOS;
memset(now,-1,sizeof(now));
memset(nxt,-1,sizeof(nxt));
int n,m;
cin>>n>>m;
string s,t;
cin>>s;
int len=s.size();
for(int i=len-1; i>=0; i--)
{
for(int j=0; j<26; j++)
nxt[i][j]=now[j];
now[s[i]-'a']=i;
}//序列自动机初始化
for(int i=0; i<m; i++)
{
cin>>t;
bool flag=true;
len=t.size();
int lac=now[t[0]-'a'];
if(lac==-1)
{
printf("NO\n");
}
else
{
for(int i=0; i<len; i++)
{
lac=nxt[lac][t[i]-'a'];
if(lac==-1)
{
flag=false;
break;
}
}
if(flag)printf("YES\n");
else printf("NO\n");
}
}
return 0;
}