BOJ 10745 Censoring (Gold)
sorohue가 PS하는 블로그
BOJ 10745 Censoring (Gold)
문제 링크입니다.
10747번 문제를 먼저 풀어 봅시다.
문제 요약
주어진 문자열 $S$에서 $N$개의 패턴 $P_1, P_2, \cdots, P_N$을 앞에서부터 찾는 대로 지워나갑니다. 최종 문자열을 구하세요.
풀이
$S$에서 여러 개의 패턴을 동시에 찾는 건 아호-코라식으로 슥슥하면 됩니다. 찾은 패턴을 $S$에서 날려버린 뒤의 처리만 고민해 보면 됩니다.
아호-코라식은 근본적으로 트리 위에서 KMP를 달리는 것과 다르지 않습니다. 마찬가지로 다음 문자가 틀려먹었을 때 어디까지 돌아가야 되는 지를 실패 함수라고 이름을 붙여서 관리합니다. S를 순회하면서 벌어지는 상태 전이는 여전히 incremental하니 패턴을 찾으면 없던 셈 치고 마저 아호-코라식을 진행하면 됩니다.
10747번 문제에서는 드러나지 않았는데, 단순히 문자열을 뒤로 감는 식으로만 처리하면 직전 문자에서 실패 함수를 따라 어디까지 뒤로 가야 되는 지를 매번 계산하게 되어서 비효율적입니다. 이를 해결하기 위해 각 상태마다 다음 문자가 뭐냐에 따라 실제로 어디까지 되돌아가게 되었는 지를 적당히 저장해 두고 갖다 쓰도록 구현해 줍시다. (아래 코드에서는 FAIL이 해당 기능을 해 줍니다.)
총 시간 복잡도는 ${\cal O}((\vert S \vert + \Sigma \vert P \vert) \lg \Sigma \vert P \vert)$입니다.
코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
using pii = pair<int, int>;
struct trienode{
int child[26], fail, w;
trienode(){
w = fail = 0;
for(int i = 0; i < 26; i++) child[i] = -1;
}
};
struct Trie{
vector<trienode> t;
map<pii, int> FAIL;
int _newNode(){
t.push_back(trienode());
return t.size()-1;
}
Trie(){_newNode();}
int clear(){
t.clear();
t.push_back(trienode());
return t.size()-1;
}
void add(string& str, int w = 1){
int node = 0;
for(auto& s : str){
int c = s-'a';
if(t[node].child[c] == -1) t[node].child[c] = _newNode();
node = t[node].child[c];
}
t[node].w += w;
}
void bfs(){
queue<int> q;
q.push(0);
t[0].fail = 0;
while(q.size()){
int now = q.front(); q.pop();
for(int i = 0; i < 26; i++){
if(t[now].child[i] == -1) continue;
int nxt = t[now].child[i];
if(!now) t[nxt].fail = 0;
else{
int prev = t[now].fail;
while(prev && (t[prev].child[i] == -1)) prev = t[prev].fail;
if(t[prev].child[i] != -1) prev = t[prev].child[i];
t[nxt].fail = prev;
}
q.push(nxt);
t[nxt].w += t[t[nxt].fail].w;
}
}
}
int transit(int now, int c){
if(FAIL.find({now, c}) != FAIL.end()) return FAIL[{now, c}];
pii state = {now, c};
while(now && t[now].child[c] == -1) now = t[now].fail;
if(t[now].child[c] != -1) return FAIL[state] = t[now].child[c];
return FAIL[state] = 0;
}
void solve(string& str){
int now = 0;
vector<char> ans;
stack<int> rollback;
rollback.push(0);
for(auto& c : str){
ans.push_back(c);
now = transit(now, c-'a');
rollback.push(now);
if(t[now].w){
for(int i = 0; i < t[now].w; i++){
rollback.pop(); ans.pop_back();
}
now = rollback.top();
}
}
for(auto& c : ans) cout << c;
}
} trie;
int main(){
cin.tie(0);cout.tie(0);ios::sync_with_stdio(false);
string s; cin >> s;
int n; cin >> n;
while(n--){string p; cin >> p; trie.add(p, p.size());}
trie.bfs();
trie.solve(s);
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.