문자열 - 아호 코라식(Aho-Corasick)
아호 코라식(Aho-Corasick)에 대해 알아보자.
[18719 Binomal] https://www.acmicpc.net/problem/18719 .
SOS DP를 이용하여 부분집합의 합을 빠르게 구해보자.
다이아 문제를 탐색하던 중 5개월 전에 풀다가 ‘당연하게도’ 실패한 Binomal문제가 눈에 들어왔고 열심히 다시 풀어봤는데 안됐다 ㅠㅠ
블로그를 탐색해보니 SOS dp를 이용하는 것이라고 하여 이를 찾아봤다.
그랬더니 binomal문제가 아름답게 보였다 ㄷㄷ 이렇게 깔끔하게 홀수임을 판별하다니 ㅠㅠ
이에 대해서 알아보자
배열이 주어지고 그 배열에서 한쌍 (i, j) 를 뽑아서 이항계수를 만들었을 때 그것이 홀수인 쌍의 개수를 구하는 문제이다.
이를 5개월 전(골드일때인가?) 시도 했을 때는 이항계수를 2로 나눈 값을 전처리해서 풀려고 했었고 지금 보니 O(n^2)으로 풀어서 당영히 시간초과다.
그럼 이 문제는 어떻게 풀어야 할까?
비트마스킹을 이용해서 부분 집합의 합을 바르게 구하는 방법이다.
예를 들어서 1011101의 부분집합의 개수를 구하고 싶으면 각자리 수를 포함하는지 안하는지를 보면서 가야한다.
일단 dp[i][k]는 i의 상태(state)와 k까지만 일치하는 것들의 부분집합의 개수이다.
말을 좀 이상하게 한 것 같은데 dp[1101][0]의 개수는 1101의 상태를 가진 것의 개수인 것이다.
dp[1101][1]은 110까지는 같은데 그 이후는 다를 수도 있는 것의 개수이다. 즉 1101또는 1100이 될 것이다.
그러니까 k-1번째 자리에서 0이면 그자리는 선택하면 안되니까
근데 k-1번째 자리가 1이면 그 자리는 선택해도되고 안해도 되니까
for(int i=1;i<(1<<20);i++){
for(int j=1;j<21;j++){
dp[i][j] = dp[i][j-1];
if(i&(1<<(j-1))) dp[i][j] += dp[i-(1<<(j-1))][j-1];
}
}
모든 자연수는 2로 나누면 0또는 1이다.
그리고 뤼카정리를 이용하면 이항계수를 이진수 이항계수의 곱으로 나타낼 수 있다.
이 때 하나라도 0이 있으면 곱셈이기때문에 총 0이되고 이는 짝수라는 것이다.
그러니까 부분집합이여야지만 홀수가 나온다.(15C6 같은걸로 해보자)
dp[i][0] : 배열에 i가 몇개 있음?
dp[i][20] : 배열에 i보다 작거나 같은게 몇개 있음?
따라서 모든 홀수는 dp[i][0]*dp[i][20]을 곱한값을 모든 i에 대해 더해주면 된다.
Sum Over Subsets DP table을 잘 구했다면 아무 문제 없다.
나머지 주의 할 점은 코드에 주석으로 처리했다.
#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;
int n;
vector<int> a;
ll dp[1<<20][21];
int main(){
fast_io
int tt; cin>>tt;
while (tt--) {
cin >> n;
a = vector<int> (n);
for(int i=0;i<n;i++){
cin >> a[i];
dp[a[i]][0]++;
}
for(int i=1;i<(1<<20);i++){
for(int j=1;j<21;j++){
dp[i][j] = dp[i][j-1];
if(i&(1<<(j-1))) dp[i][j] += dp[i-(1<<(j-1))][j-1];
}
}
ll res = 0;
for(int i=0;i<(1<<20);i++){
res += dp[i][0]*dp[i][20];
}
cout << res << '\n';
memset(dp, 0, sizeof(dp));
}
}
아호 코라식(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