문자열 - 아호 코라식(Aho-Corasick)
아호 코라식(Aho-Corasick)에 대해 알아보자.
디비전 2는 솔직히 4문제는 풀어야 본전이다.
나도 옛날엔 인정하지 않았지만 A == 브론즈, B== 실버하위, C == 실버 상위, D == 골드 수준의 문제들이 출제 되기 때문이다. (물론 편차가 있긴하지만)
예전에는 내가 C, D를 자주 못풀었기 때문에 D가 무슨 골드냐 플레는 되겠지..했는데 솔직히 풀이를 보면 골드가 맞다. ㅋㅋ
내가 아직 골드는 확정적으로 푸는 실력이 안되는 것이다.
그래도 오늘은 4문제를 겨우겨우 어찌저찌 풀었으니 다행이다. E까지 업솔빙을 해보겠다.
해가 무조건 있다고 알려줬다.
그러니 k로 나누어 떨어지지 않으면 1을 출력하고 x로 바로 이동한다.
k로 나누어 떨어지면 2를 출력하고 i, x-i로 분할하여 생각한다.
#include <bits/stdc++.h>
#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;
typedef tuple<int, int, int> tiii;
#define xx first
#define yy second
int main(){
fast_io
int tt; cin >> tt;
while(tt--){
int x, k; cin >> x >> k;
if(x%k){
cout << 1 << '\n';
cout << x << '\n';
}else{
cout << 2 << '\n';
for(int i=1;i<=x;i++){
if(i%k&&(x-i)%k){
cout << i << ' ' << x-i << '\n';
break;
}
}
}
}
}
꺾은 괄호가 들어가는 문자열에서 숫자를 적절히 배열하여 올바른 식을 만들 때 , 사용할 수 있는 숫자의 최소개수를 구하는 것이다.
연속되는 « $\dots$와 »$\dots$의 개수를 세고 거기에 수를 넣는다고 생각하면 연속되는 괄호의 개수 +1이 정답이다.
#include <bits/stdc++.h>
#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;
typedef tuple<int, int, int> tiii;
#define xx first
#define yy second
int main(){
fast_io
int tt; cin >> tt;
while(tt--){
int n; cin >> n;
string s; cin >> s;
int res = 0;
char pre = 0;
int now = 0;
for(int i=0;i<s.size();i++){
if(s[i]==pre){
now++;
}else{
now = 1;
pre = s[i];
}
res = max(res, now);
}
cout << res+1 << '\n';
}
}
조금 오래 걸리긴 했다. 그 이유는 reverse의 뜻을 toggle과 혼동했기 때문이다.
0을 1로 바꾸고 1을 0으로 바꾸라는 줄 알았는데 알고보니 substring을 그냥 뒤집는 거였따.
문제를 똑바로 읽기로 다짐한지 2주도 안되었는데 바로 착각해버렸다.
솔직히 영어를 못하는 문제도 조금은 있는 것 같다.
결국 이 문제는 물음표 자리에 바로 이전 것이 들어가면 된다.
문제를 따라가느라 deque을 이용했다. (하지만 필요없을 듯하다, 그래도 연속되는 ?가 있을 수 있으니 자료구조를 이용하긴 해야할듯)
#include <bits/stdc++.h>
#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;
typedef tuple<int, int, int> tiii;
#define xx first
#define yy second
int main(){
fast_io
int tt; cin >> tt;
while(tt--){
string s; cin >> s;
deque<char> dq;
dq.push_back('0');
string res;
for(int i=0;i<s.size();i++){
if(s[i]=='0'){
dq.push_back('0');
}else if(s[i]=='1'){
if(dq.front()=='0'){
while(dq.front()!='0') {
res += dq.front();
dq.pop_front();
}
}else{
if(dq.back()=='0'){
while(!dq.empty()){
res += dq.front();
dq.pop_front();
}
}
}
dq.push_back('1');
}else{
dq.push_back(dq.back());
}
}
while(!dq.empty()){
res += dq.front();
dq.pop_front();
}
for(int i=1;i<res.size();i++) cout << res[i];
cout << '\n';
}
}
이 풀이 말고 관찰을 통해 위 사실을 알아낸뒤
솔직히 이 문제를 이렇게나 늦게 푼 것은 온전히 해석 탓이다.
정말 화가 난다. 문제를 제대로 읽지 못해 시간 끈 것이 이 대회만 해도 2개라니….
영어 공부한다고 이런 습관이 고쳐지기는 쉽지 않을 것이다.
문제를 이해 하면 다음과 같다.
따라서 위 내용 중 가장 핵심은
regular랑 beautiful이랑 섞이면 절대로 beautiful이 될 수 없다는 것이다.
그래서 이 문제는 regular와 beautiful을 따로 색칠하는 문제가 된다.
따라서 될 수 있는 Color의 최대값은 2이다.
나는 그냥 beautiful을 뒤집어서 beautiful이면 되는 줄 알았는데….이따구로 이해해서 4틀을 한 내가 너무 한심하다.
하지만 그래도 시간안에 올바르게 이해해서 다시 풀었으니 봐주도록 해야겠따.
(안봐줬을 때 페널티로 할 것도 없음ㅋㅋ)
#include <bits/stdc++.h>
#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;
typedef tuple<int, int, int> tiii;
#define xx first
#define yy second
int main(){
fast_io
int tt; cin >> tt;
while(tt--){
int n; cin >> n;
string s; cin >> s;
vector<int> fr, bk;
for(int i=0;i<n;i++){
if(s[i]=='(') fr.push_back(i);
else bk.push_back(i);
}
if(fr.size()!=bk.size()){
cout << -1 << '\n';
continue;
}
set<int> ST;
vector<int> res(n);
deque<char> dq;
int k1 = 0, k2 = 0;
for(int i=0;i<n;i++){
if(dq.empty()||dq.back()==s[i]) {
if(s[i]=='('&&i<fr[k1]) continue;
if(s[i]==')'&&i<bk[k2]) continue;
dq.push_back(s[i]);
}
else{
ST.insert((dq.front()=='('));
while(!dq.empty()){
res[fr[k1]] = (dq.front()=='(');
res[bk[k2]] = (dq.front()=='(');
k1++; k2++;
dq.pop_front();
}
}
}
cout << ST.size() << '\n';
for(int ANS : res){
if(ST.size()==1) cout << 1 << ' ';
else if(ANS==0) cout << 1 << ' ';
else cout << 2 << ' ';
}
cout << '\n';
}
}
문제를 정리해보면 1번팀이 제일 세고 $2^k$번째 팀이 가장 약하다.
이 때 토너먼트는 seed를 받고 그에 따라 결정된다. (1-2번 시드끼리, 3-4번 시드끼리 ….)
토너먼트가 실력대로 결과가 나오도록 조작할 수 있을 때 그렇게 만들 수 있는 경우의 수를 구한다.
예를 들면
1 2 3 4 라면 1번 2번팀이 첫라운드에 싸우니까 절대로 실력대로 결과가 나오지 않는다.
-1 -1 -1 -1 이라면 1번팀은 4군데중 아무데나 갈 수 있고 2번팀은 그 반대라인으로 가면 2개 중하나가 된다. 그리고 3, 4를 배치하는경우의 수가 2개니까 $4 \times 2 \times 2$ 가 된다.
위 내용이 문제 정리다. 그럼 어떻게 풀어야 할까?
이번 턴에 지는 사람 $2^{k-1}$의 배치는 맘대로 할 수 있다. (-1인 경우에)
그래서 지는 사람이 -1이라면 이들의 배치를 신경써야한다.
#include <bits/stdc++.h>
#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;
typedef tuple<int, int, int> tiii;
#define xx first
#define yy second
const int MOD = 998244353;
int main(){
fast_io
int K; cin >> K;
vector<int> a(1<<K);
for(int i=0;i<(1<<K);i++){
cin >> a[i];
if(a[i]!=-1) --a[i];
}
ll res = 1;
for(int i=K-1;i>=0;i--){
int big = (1 << i);
int minus_one = 0;
vector<int> na(1 << i);
for(int j=0;j<(1<<i);j++){
if(a[2*j]>a[2*j+1]) swap(a[2*j], a[2*j+1]); // 앞이 이김
if(a[2*j]==-1){
if(a[2*j+1]>= (1 << i)){ //앞이 이기는 경우가 맞음
big--;
na[j] = -1;
}else if(a[2*j+1]!=-1){ //이건 뒤가 이기는 경우임
na[j] = a[2*j+1];
}else{ //배치를 맘대로 해도 됨
na[j] = -1;
minus_one++;
}
continue;
}
if( a[2*j]<(1<<i) == a[2*j+1]< (1 << i)){ //둘다 기준보다 작으면 안됨
cout << 0;
return 0;
}
na[j] = a[2*j]; //정상적으로 앞이 이김
big--;
}
for(int i=0;i<minus_one;i++) res = res * 2LL % MOD; // 둘의 배치를 바꿀 수 있음
for(int i=1;i<=big;i++) res = 1LL*res * i %MOD; //이긴 것들의 순서 조정
a = na;
}
cout << res;
}
재밌는 문제였따.
토너먼트 문제는 이긴 사람, 진 사람을 제대로 나누고 생각하는 것이 중요해 보인다.
아호 코라식(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