요약: 집합 S(크기 \(N\))의 문자열들 중, 질의 문자열의 “부분 문자열"로 등장하는 것이 하나라도 있으면 YES, 아니면 NO를 출력합니다. 모든 문자열은 소문자.
제한: \(1 \le N,Q \le 1000\), 패턴 길이 ≤ 100, 질의 길이 ≤ 10000, 메모리 256MB, 시간 1초.
입력/출력
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<입력>
N
S1
S2
...
SN
Q
T1
T2
...
TQ
<출력>
각 질의 Ti에 대해 YES 또는 NO
접근 개요
핵심: N개의 패턴을 트라이에 넣고 아호-코라식(Aho-Corasick) 실패 링크를 구축해 하나의 자동자로 통합합니다.
각 질의 문자열을 선형으로 주행하면서 현재 상태에서의 전이/실패 링크를 따라가고, 매칭 상태(out)에 도달하면 즉시 YES입니다.
알파벳 26개 고정 전이를 사용해 상수 시간 전이를 보장, 전체는 입력 총 길이 선형 시간에 동작합니다.
flowchart LR
A[트라이 구성 (N개 패턴 삽입)] --> B[실패 링크 BFS 구축]
B --> C[output 전파 (fail을 통해 매칭 상태 상속)]
C --> D[각 질의 문자열 선형 주행]
D -->|매칭(out=true) 도달| E[YES]
D -->|끝까지 매칭 없음| F[NO]
// 더 많은 정보는 42jerrykim.github.io 에서 확인하세요.
#include<bits/stdc++.h>usingnamespacestd;structNode{intnext[26];intfail;boolout;Node(){fill(begin(next),end(next),-1);fail=0;out=false;}};intmain(){ios::sync_with_stdio(false);cin.tie(nullptr);intN;if(!(cin>>N))return0;vector<Node>trie(1);// root = 0
autoadd_pattern=[&](conststring&s){intu=0;for(charch:s){intc=ch-'a';if(trie[u].next[c]==-1){trie[u].next[c]=(int)trie.size();trie.emplace_back();}u=trie[u].next[c];}trie[u].out=true;};for(inti=0;i<N;++i){strings;cin>>s;add_pattern(s);}// Build failure links (Aho-Corasick)
queue<int>q;for(intc=0;c<26;++c){intv=trie[0].next[c];if(v==-1)trie[0].next[c]=0;else{trie[v].fail=0;q.push(v);}}while(!q.empty()){intv=q.front();q.pop();for(intc=0;c<26;++c){intu=trie[v].next[c];if(u==-1){trie[v].next[c]=trie[trie[v].fail].next[c];}else{trie[u].fail=trie[trie[v].fail].next[c];trie[u].out=trie[u].out||trie[trie[u].fail].out;q.push(u);}}}intQ;cin>>Q;while(Q--){stringt;cin>>t;intstate=0;boolfound=false;for(charch:t){intc=ch-'a';state=trie[state].next[c];if(trie[state].out){found=true;break;}}cout<<(found?"YES":"NO")<<'\n';}return0;}
# 더 많은 정보는 42jerrykim.github.io 에서 확인하세요.importsysfromcollectionsimportdequeinput=sys.stdin.readlineclassNode:__slots__=("nxt","fail","out")def__init__(self):self.nxt=[-1]*26self.fail=0self.out=Falsedefsolve():N_line=input().strip()ifnotN_line:returnN=int(N_line)trie=[Node()]# root = 0defadd_pattern(s:str):u=0forchins:c=ord(ch)-97iftrie[u].nxt[c]==-1:trie[u].nxt[c]=len(trie)trie.append(Node())u=trie[u].nxt[c]trie[u].out=Truefor_inrange(N):add_pattern(input().strip())# build failure linksq=deque()forcinrange(26):v=trie[0].nxt[c]ifv==-1:trie[0].nxt[c]=0else:trie[v].fail=0q.append(v)whileq:v=q.popleft()vf=trie[v].failforcinrange(26):u=trie[v].nxt[c]ifu==-1:trie[v].nxt[c]=trie[vf].nxt[c]else:trie[u].fail=trie[vf].nxt[c]iftrie[trie[u].fail].out:trie[u].out=Trueq.append(u)Q=int(input())out_lines=[]for_inrange(Q):t=input().strip()state=0ans="NO"forchint:state=trie[state].nxt[ord(ch)-97]iftrie[state].out:ans="YES"breakout_lines.append(ans)sys.stdout.write("\n".join(out_lines))if__name__=="__main__":solve()
코너 케이스 체크리스트
패턴 중복, 서로의 접두사/접미사 관계(실패 링크 전파로 처리됨)
질의 내 다중 매칭 발생(첫 매칭에서 조기 종료 가능)
알파벳 범위 확인(소문자 a-z 이외 입력 없음 가정)
빈 패턴은 미출현(문제 조건), 질의는 길이 ≥ 1 가정
제출 전 점검
빠른 입출력 활성화(sync_with_stdio(false), tie(nullptr)), Python은 sys.stdin.readline