문자열 - 아호 코라식(Aho-Corasick)
아호 코라식(Aho-Corasick)에 대해 알아보자.
[3308 Matching] https://www.acmicpc.net/problem/3308 .
문자열에서 패턴을 찾을 때 KMP를 이용하는데 세그트리와 접목한 문제를 풀어보자!
내 블로그에 포스팅할때도 KMP랑 Segment tree를 비슷한 시기에 올린 것 같은데 그 둘을 접목한 문제가 있다는 것을 배웠다.
ICPC신촌 캠프할 때 접한 20298 파인애플피자로 이런 유형을 알게 되었다.
이걸 풀어내니 23576 Stock Price Prediction도 풀수 있게 되었다.
결국 KMP를 할때 문자가 같은지 확인하는 부분에서 같은 꼴(비슷한 형태)인지 확인하는 형태로 바꾸면 되는 문제이다.
위에서 소개한 두 문제와 정확히 동일하지만 입력의 형태가 좀 다르다.
패턴이 주어지는데 작은 순서대로 인덱스를 주기 때문이다.
예를 들어 2 1 5 3 4처럼 주어지면 2번이 젤 작고 1번, 5번, 3번, 4번 순이라는 뜻이다.
위 그림과 같은 대소관계를 가진 그래프를 찾으면 된다.
이를 그래프 같은 모양의 숫자를 설정해서 파인애플 피자 풀듯이 풀면 끝이다.
파인애플 피자 풀듯이 풀면 끝이라고는 했지만 파인애플 피자도 포스팅을 안했다. ㅎㅎ
푸는 아이디어는 다음과 같다.
먼저 KMP의 매칭 부분을 잘 보자.
for(int i=0, j=0;i<n+k-1;i++){
while (j&&H[i]!=P[j]) j = ff[j-1]; //다르면 failure function에 따라 다시 매칭시작
if(H[i]==P[j]){
if(j==M-1){
ret++; //완벽하게 매칭 됨
j = ff[j];
}else{
j++; //계속 매칭해나감
}
}
}
return ret;
저기서 Hay 문자열과 Pattern 문자열을 비교하는 부분을 바꾸면 된다.
지금 매칭하는 것은 문자열에서의 rank이다.
그니까 1과 100이 같은 rank일지도 모른다는 것이다.
예를 들면 다음과 같은 것이다.
1 2 3 4 5 와 100 200 300 400 500에서 1과 100은 같은 것이다.
그 이유는 1보다 큰게 패턴에서 4개, 100보다 큰게 4개로 같기 때문이다.
그니까 비교를 할 때 비교대상보다 작은것의 개수, 큰 것의 개수를 세어서 그게 일치하면 같은 문자로 보는 것이다.
세그먼트 트리는 구간에 원소가 몇개 있는지를 Log시간에 셀 수 있으니 이를 이용한다.
근데 이문제는 세그트리 안쓰고 O(N+M)에 풀 수 있다.
중복되는 문자(패턴)가 없기 때문에 25008 문자열 찾기이 문제 처럼 풀 수 있기 때문이다.
하지만 연습삼아 세그트리로 풀꺼다.(근데 시간초과 한번 떴음 ㅠㅠ)
논리는 다음과 같다.
일단 나는 재귀 세그를 짰더니 시간초과가 떴다 ㅠㅠ 비재귀 세그로 다시 짜서 내보니 됐다.
나머지 주의 할 점은 코드에 주석으로 처리했다.
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#include <stack>
#include <queue>
#include <cmath>
#include <set>
#include <map>
#include <cassert>
#define fast_io cin.tie(NULL); ios_base::sync_with_stdio(false);
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int MAX = 1000001;
struct Fenwick{
int n;
int t[2 * MAX];
void modify(int p, int value) { // plus value at position p
for (t[p += n] += value; p > 1; p >>= 1) t[p>>1] = t[p] + t[p^1];
}
int query(int l, int r) { // sum on interval [l, r)
int res = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if (l&1) res += t[l++];
if (r&1) res += t[--r];
}
return res;
}
void clear(){
for(int i=0;i<2*MAX;i++) t[i] = 0;
}
};
int m, n;
vector<int> x, y, X, Y, res, front, back, temp;
Fenwick fwt;
bool Match(int i, int j, int sz){
if(front[j]!=fwt.query(0, i)) return false;
if(back[j]!=fwt.query(i+1, MAX)) return false;
return true;
}
vector<int> getFail(vector<int> &P){
int sz = P.size();
vector<int> Fail(sz);
for(int i=0;i<sz;i++){
front[i] = fwt.query(0, P[i]);
back[i] = fwt.query(P[i]+1, MAX);
fwt.modify(P[i], 1);
}
fwt.clear();
for(int i=1, j=0;i<sz;i++){
while (j&&!Match(P[i], j, sz)) {
for(int k=i-j;k<i-Fail[j-1];k++) fwt.modify(P[k], -1);
j = Fail[j-1];
}
if(Match(P[i], j, sz)){
Fail[i] = ++j;
fwt.modify(P[i], 1);
}
}
return Fail;
}
void KMP(vector<int> &H, vector<int> &P){
int hsz = H.size(), psz = P.size();
vector<int> ff = getFail(P);
fwt.clear();
for(int i=0, j=0;i<hsz;i++){
while (j&&!Match(H[i], j, hsz)) {
for(int k=i-j;k<i-ff[j-1];k++) fwt.modify(H[k], -1);
j = ff[j-1];
}
if(Match(H[i], j, hsz)){
if(j==psz-1){
res.push_back(i-psz+2);
for(int k=i-j;k<i-ff[j]+1;k++) fwt.modify(H[k], -1);
fwt.modify(H[i], 1);
j = ff[j];
}else{
j++;
fwt.modify(H[i], 1);
}
}
}
}
void compress(){
sort(x.begin(), x.end());
x.erase(unique(x.begin(), x.end()) ,x.end());
for(int i=0;i<m;i++) X[i] = lower_bound(x.begin(), x.end(), X[i])-x.begin()+1;
sort(y.begin(), y.end());
y.erase(unique(y.begin(), y.end()), y.end());
for(int i=0;i<n;i++) Y[i] = lower_bound(y.begin(), y.end(), Y[i])-y.begin()+1;
}
int main(){
fast_io
cin >> m >> n;
x = vector<int> (m); X = vector<int> (m);
y = vector<int> (n); Y = vector<int> (n);
temp = vector<int> (m);
for(int i=0;i<m;i++){
cin >> temp[i];
x[temp[i]-1] = X[temp[i]-1] = i+1;
}
for(int i=0;i<n;i++){
cin >> y[i]; Y[i] = y[i];
}
compress();
front = vector<int> (m);
back = vector<int> (m);
fwt.n = MAX;
KMP(Y, X);
cout << res.size() << '\n';
for(int i=0;i<res.size();i++) cout << res[i] << ' ';
}
아호 코라식(Aho-Corasick)에 대해 알아보자.
docker를 이해해보자
2060 염소줄서기 풀이 및 코드
분할정복을 이용한 다이나믹 프로그래밍 최적화
코드포스 다시 열심히! 블로그도 열심히!
rust 공부시작!
코드포스 블루 달성 후기
여름캠프 및 SUAPC 후기
2023-05-25-Edu Codeforce round 149 (Div.2)
2023-05-13-Edu Codeforce round 148 (Div.2)
HLD(Heavy Light Decomposition)
행렬 거듭 제곱
2023-05-02-It takes two
Codeforce round 868 (Div.2)
2월 11일 문제풀이
Codeforces#846, TypeDB Foreces 2023, Codeforces#848 업솔빙
ps5 게임 : 용과같이 제로 리뷰
백준 23877번 Convoluted Intervals 문제풀이
codeforce round #828(div 3), EDU #137(div 2) 업솔빙
codeforce round #823(div 2), #824(div 2) 업솔빙
AtCoder Beginner Contest 270 업솔빙
ps5 게임 : 용과같이 극 1 리뷰
백준 18719번 Binomal 문제풀이
백준 14288번 회사문화4 문제풀이
백준 3308번 Matching 문제풀이
백준 18186번 라면사기(large) 문제풀이
백준 4196번 도미노 문제풀이
백준 3176번 도로 네트워크 문제풀이
백준 16367번 TV Show Game 문제풀이
ps5 게임 : 페르소나 5 더 로열 리뷰
codeforce round #811(div 3), #812(div 2), CodeTon round 2 업솔빙
백준 21162번 뒤집기 K 문제풀이
codeforce round #808(div 2), #803(div 2, virtual) 업솔빙
codeforce round #807(div 2) 업솔빙
백준 10167번 금광 문제풀이
codeforce round #805(div 3), #806(div 4) 업솔빙
백준 18253번 최단경로와 쿼리 문제풀이
에듀 라운드 131 업솔빙
백준 1949번 우수 마을 문제풀이
백준 3665번 최종 순위 문제풀이
Trie 자료구조 이해하기
merge sort를 이용하여 inversion 개수세기
3솔의 벽이 너무 높다..
lazy propagation없이 구간 갱신하기
코드포스 폭망기념 upsolving
백준 11505 구간 곱 구하기 문제풀이
백준 1305 광고 문제풀이
백준 4386 별자리 만들기 문제풀이
백준 4803 트리
백준 2206 벽 부수고 이동하기 문제풀이
백준 2166 다각형의 면적 문제풀이
백준 12015 가장 긴 증가하는 부분 수열2 문제풀이
백준 10986 나머지합 문제풀이
스택 구현하기 ========== 자료구조의 기본이라고 하면 스택 과 큐가 있다 백준 10828번에서 마주친 스택
매우매우 많은 정렬이 있지만 그중 가장 안 어려운 3가지를 공부해보았다.
Leave a comment