牛客 子串查询(序列自动机)

链接: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;
}
发布了41 篇原创文章 · 获赞 2 · 访问量 1243

猜你喜欢

转载自blog.csdn.net/qq_41418281/article/details/100587927