<题目链接>
题目大意:
给你一个母串A,然后进行q次询问,每次询问给定一个长度不超过母串的字符串,问你这个字符串是否是母串的子串。
解题分析:
序列自动机模板题。本题的思想就是考虑贪心的去匹配,我们希望当前匹配到的位置越靠前越好。所以对于母串每一位都记一下下一次出现某个字符的位置。匹配的时候从第零位(虚根)开始,如果能一直匹配下去就是$Yes$ ,否则就是 $No$,这些操作就是用序列自动机简单实现。
单次查询的复杂度是$O(|B_i|)$ 的,序列自动机预处理的复杂度是 $O(26|A|)$的。总时间复杂度是$O(26|A|+\sum_{i=1}^N |B_i|)$。
序列自动机
#include<bits/stdc++.h> using namespace std; const int N = 1e6+5; int n,m,T; char str[N],s[N]; int nxt[N][26],pos[26]; void init(){ //预处理主串的信息 n=strlen(str+1); for(int i=0;i<26;i++) pos[i]=n+1; //a~z中每个字母的下一个字母所对应的位置 for(int i=n;~i;i--){ for(int j=0;j<26;j++) nxt[i][j]=pos[j]; //记录下下一次出现这个字符的位置 pos[str[i]-‘a‘]=i; //更新a[i]最对应的字母对应的当前最靠前的位置 } } bool solve(){ scanf("%s",s+1); m=strlen(s+1);int x=0; //x从0开始,0作为虚根 for(int i=1;i<=m;i++){ int v=s[i]-‘a‘; if(nxt[x][v]==n+1) return false; //如果下一个出现字符的位置不存在,则说明该子串并未完全匹配 x=nxt[x][v]; } return true; } int main(){ scanf("%s",str+1); cin>>T;init(); while(T--) solve()?puts("Yes"):puts("No"); }
SAM:
#include <bits/stdc++.h> using namespace std; const int MaxN = 1e6 + 5; int N, L; char str[MaxN], str2[MaxN]; struct SAM { int cntv; int nxt[MaxN]; int last[26], ch[26][MaxN]; SAM() { nxt[0] = -1; } inline void insert(int c) { cntv++; nxt[cntv] = last[c]; for (int i = 0; i < 26; ++i) for (int p = last[i]; p != -1 && ch[c][p] == 0; p = nxt[p]) ch[c][p] = cntv; last[c] = cntv; } inline bool check(int u, int x) { if (x == L) return true; int c = str2[x] - ‘a‘; if (ch[c][u] == 0) return false; return check(ch[c][u], x + 1); } } sam; void init() { scanf("%s", str); scanf("%d", &N); for (int i = 0; str[i]; ++i) sam.insert(str[i] - ‘a‘); } void solve() { for (int i = 1; i <= N; ++i) { scanf("%s", str2); L = strlen(str2); puts(sam.check(0, 0) ? "Yes" : "No"); } } int main() { init(); solve(); }
Nowcoder contest 392 J (模板题)【序列自动机】
原文:https://www.cnblogs.com/00isok/p/10503988.html