티스토리 뷰
문제 링크 : https://www.acmicpc.net/problem/5905
5905번: 악당 로봇
슈퍼히어로 박승원은 결국 지구를 침략한 악당 로봇들을 해킹하는 데 성공했다. 로봇은 N개의 취약점을 가지고 있는데, (1 ≤ N ≤ 20) i번째 취약점은 'A', 'B', 'C' 로만 구성된 15자 이하의 문자열 S
www.acmicpc.net
'콤보' 라는 이름으로 패턴들이 주어진다.
검색 대상 스트링이 주어지고 거기에 콤보가 몇 번이나 발생한 것인지를 묻는다면, 평범한 스트링 문제일 것이다.
하지만 길이 K 이하의 문자열로 최대의 콤보가 발생하게 만들어야 하는 상황이니 dp 외에 뚜렷한 방법이 생각나지 않는다.
1. 필요한 정보들
(1) added[i][j]
added[i][j] :콤보 i 에 1 문자 이상을 더해서 콤보 j 로 끝나는 문자열을 만드는 최소의 길이.
예를 들어 combo[i]='xbcbc' 이고 combo[j]='bcbcy' 일 때,
combo[i]+'y' 혹은 combo[i]+'bcy' 는 모두 combo[j] 로 끝나는 문자열이 된다.
여기서는 최소의 것이라 했으니 added[i][j]=1 이 된다.
여기서 우리가 배제한 'bcy' 를 더하는 경우는 added[j][j] 에서 반영될 것이므로 걱정할 것 없다.
(2) contains[i][rIdx]
contains[i][rIdx] : combo[i] 에서 뒤에서부터 rIdx 위치 까지에서 끝나는 다른 콤보의 누적 개수.
써놓고 보니까 국어를 잘 못한다는 사실을 새삼 깨닫게 된다. 별 수 없다 예를 보자.
combo[i]='abc' 라 하자. rIdx 는 뒤쪽에서 시작한 인덱스이므로 rIdx=1 은 마지막 문자까지 일치한 콤보 개수의 합산이다.
예를 들어 combo[j]='bc' 이면 contains[i][1] 에 1 기여하게 된고 combo[j]='ab' 이면 contains[i][2] 에 1 기여한다.
참고로 이 단계에서 Aho-Corasick 알고리즘을 적용할 수 있지만 backward 누적 계산까지 처리하는 방법은 찾지 못했다.
콤보의 개수가 최대 20 개이고 각각의 길이가 최대 15 이므로 모두 직접 조사해도 20*20*15 이어서 그냥 무식하게 계산했다.
2. Trainsition
dp 를 다음과 같이 정의하자.
dp[k][i] : 현재까지 입력된 문자열의 길이가 k 이고 i 번 콤보로 끝날 때 누적 콤보 개수의 최대값.
문자열을 만들어갈 때 콤보로 끝나지 않으면 세어질 이유가 없음은 이해가 될 것이다.
현재의 길이 k 보다 길면서 j 콤보로 끝나는 문자열은 다음과 같이 될 것이다.
target = k+added[i][j];
dp[target][j]=max( dp[target][j], dp[k][i]+contains[j][added[i][j]] )
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N, K;
struct Combo{
char word[16];
int len;
};
Combo combo[21];
bool suffix_prefix_match(int i,int j, int len) {
int start = combo[i].len-(combo[j].len-len);
return strncmp(combo[i].word+start,
combo[j].word,
combo[j].len-len) == 0;
}
int added[21][21];
void calculate_transition_length(){
for(int i=0;i<N;i++) for(int j=0;j<N;j++){
int added_length=max(1,combo[j].len-combo[i].len);
for(;added_length<=combo[j].len; added_length++)
if(suffix_prefix_match(i,j,added_length)){
added[i][j]=added_length;
break;
}
}
}
int contains[21][16];
void count_combo(){
for (int i=0; i< N; i++) {
contains[i][0] = 0;
for (int last=combo[i].len, rIdx=0; last>=0; last--, rIdx++) {
int cnt=0;
for (int j=0; j<N; j++)
if (last<combo[j].len) continue;
else if(strncmp(combo[i].word+last-combo[j].len,
combo[j].word,
combo[j].len) == 0)
cnt++;
contains[i][rIdx+1]=contains[i][rIdx]+cnt;
}
}
}
int dp[1001][21];
void solve(){
memset(dp, -1, sizeof(dp));
for (int i = 0; i < N; i++)
dp[combo[i].len][i] = contains[i][combo[i].len];
int ans = 0;
for (int k = 0; k <= K; k++) for (int i = 0; i < N; i++)
if (dp[k][i] >= 0) {
ans = max(ans, dp[k][i]);
for (int j = 0; j < N; j++) {
int target = k + added[i][j];
if (target <= K)
dp[target][j] = max(dp[target][j],
dp[k][i] + contains[j][added[i][j]]);
}
}
printf("%d\n", ans);
}
int main(){
scanf("%d %d", &N, &K);
for (int i = 0; i < N; i++){
scanf("%s", combo[i].word);
combo[i].len = strlen(combo[i].word);
}
count_combo();
calculate_transition_length();
solve();
return 0;
}
아무래도 설명히 심히 구리다.
차후에 그림을 추가해서 조금 더 상세히 기술하도록 하겠다.
'DS\Algo' 카테고리의 다른 글
BOJ 14888 연산자 끼워넣기 (0) | 2022.12.19 |
---|---|
BOJ 1083 소트 (0) | 2022.11.11 |
BOJ 2477 참외밭 (2) | 2022.10.30 |
BOJ 1517 버블 소트 (0) | 2022.10.26 |
BOJ 2357 최솟값과 최댓값 (0) | 2022.10.25 |
- Total
- Today
- Yesterday
- nearest common ancestor
- Shell Programming
- RUBY
- javascript array
- stack
- 백준
- math font
- script
- 다익스트라
- lazy propagation
- shell
- max flow
- Aho-Corasick
- C++ big number
- dynamic programming
- Reference
- bash
- BOJ
- number theory
- python3
- Vim
- 정수론
- JavaScript
- persistent segment tree
- segment tree
- bash script
- Dijkstra
- 세그먼트 트리
- fenwick tree
- map
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |