백준/5001 - 10000
[백준] 9250번 : 문자열 집합 판별
lms0806
2024. 10. 13. 18:14
728x90
반응형
https://www.acmicpc.net/problem/9250
해당 문제는 N개의 문자들이 Q번 입력받은 문자열들의 부분집합으로 포함되어 있는지 확인하는 문제입니다.
Trie와 KMP를 활용한 aho-corasick이라는 알고리즘 통해 해결항 수 있습니다.
- 트리에 insert
void insert(String word) {
Trie curNode = this;
for(int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
curNode.child.putIfAbsent(c, new Trie());
curNode = curNode.child.get(c);
if(i == word.length() - 1) {
curNode.output = true;
}
}
}
- kmp의 fail함수를 트리형태로
public void computeFailFunc() {
Queue<Trie> queue = new LinkedList<>();
this.fail = this;
queue.add(this);
while(!queue.isEmpty()) {
Trie cur = queue.poll();
for(int i = 0; i < 26; i++) { // 모든 알파벳이 포함된 문자열 탐색이니 a ~ z
char c = (char)(i + 97);
Trie nxt = cur.child.get(c);
if(nxt == null) continue;
if(cur == this) { // 1레벨 노드인 경우
nxt.fail = this;
}else {
Trie failLinkNode = cur.fail; // 부모의 실패 노드
while(failLinkNode != this && failLinkNode.child.get(c) == null) {
failLinkNode = failLinkNode.fail;
} // 부모의 실패 노드의 자식 노드가 c가 아닌 경우 거슬러 탐색
if(failLinkNode.child.get(c) != null) {
failLinkNode = failLinkNode.child.get(c);
} // 부모의 실패 노드의 자식 노드가 c인 경우 연결
nxt.fail = failLinkNode;
}
if(nxt.fail.output) {
nxt.output = true;
}
queue.add(nxt);
}
}
}
- 검색
public boolean ahoCorasick(String word) {
Trie curNode = this;
for(int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
while(curNode != this && curNode.child.get(c) ==null) {
curNode = curNode.fail;
}
if(curNode.child.get(c)!=null) {
curNode = curNode.child.get(c);
}
if(curNode.output) {
return true;
}
}
return false;
}
- 전체코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
public class Main {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
Trie trieSet = new Trie();
for(int i = 0; i < n; i++) {
trieSet.insert(br.readLine());
}
trieSet.computeFailFunc();
int q = Integer.parseInt(br.readLine());
StringBuilder sb = new StringBuilder();
for(int i = 0; i < q; i++) {
sb.append(trieSet.ahoCorasick(br.readLine()) ? "YES" : "NO").append("\n");
}
System.out.print(sb);
}
}
class Trie {
Map<Character, Trie> child = new HashMap<>();
boolean output;
Trie fail;
void insert(String word) {
Trie curNode = this;
for(int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
curNode.child.putIfAbsent(c, new Trie());
curNode = curNode.child.get(c);
if(i == word.length() - 1) {
curNode.output = true;
}
}
}
public void computeFailFunc() {
Queue<Trie> queue = new LinkedList<>();
this.fail = this;
queue.add(this);
while(!queue.isEmpty()) {
Trie cur = queue.poll();
for(int i = 0; i < 26; i++) { // 모든 알파벳이 포함된 문자열 탐색이니 a ~ z
char c = (char)(i + 97);
Trie nxt = cur.child.get(c);
if(nxt == null) continue;
if(cur == this) { // 1레벨 노드인 경우
nxt.fail = this;
}else {
Trie failLinkNode = cur.fail; // 부모의 실패 노드
while(failLinkNode != this && failLinkNode.child.get(c) == null) {
failLinkNode = failLinkNode.fail;
} // 부모의 실패 노드의 자식 노드가 c가 아닌 경우 거슬러 탐색
if(failLinkNode.child.get(c) != null) {
failLinkNode = failLinkNode.child.get(c);
} // 부모의 실패 노드의 자식 노드가 c인 경우 연결
nxt.fail = failLinkNode;
}
if(nxt.fail.output) {
nxt.output = true;
}
queue.add(nxt);
}
}
}
public boolean ahoCorasick(String word) {
Trie curNode = this;
for(int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
while(curNode != this && curNode.child.get(c) ==null) {
curNode = curNode.fail;
}
if(curNode.child.get(c)!=null) {
curNode = curNode.child.get(c);
}
if(curNode.output) {
return true;
}
}
return false;
}
}
728x90
반응형