https://www.acmicpc.net/problem/11585
11585번: 속타는 저녁 메뉴
수원이와 친구들은 저녁 메뉴를 잘 선택하지 못한다. 배가 고픈 수원이가 보다 못해 메뉴를 정하곤 하는데 이마저도 반대에 부딪히는 경우에는 수원이가 원형 룰렛을 돌려 결정하곤 한다. 이 원
www.acmicpc.net
KMP 알고리즘 문제
처음에 한칸한칸 모두 확인하니 75% 쯤에서 시간초과 발생
알고리즘 시간에 배운 KMP 알고리즘으로 해결해야 O(N+M) 시간으로 시간초과가 발생하지 않는다.
분명 머리로는 이해하고 납득했는데 코드가 이해가 안가서 한참 생각한 문제._.
푸는 방법을 글로 써내려가면,
환형 문자열의 문제들은 문자열을 두배해서 문제를 푸는 것이 일반적이라고 한다.
'I W A N T M E A T' 의 문자열을 'I W A N T M E A T I W A N T M E A T' 로 str의 문자열을 두배 늘린 2000000개 배열을 사용
그리고 KMP 알고리즘의 핵심인 pi 를 구해야한다. 문자열을 두배로 늘렸으니 pi 배열도 2000000개 사용
코드 이해하기가 정말 어려웠는데 i와 j를 포인터 (투포인터 처럼) 처럼 생각하니까 이해가 되었다.
i는 두번째 글자부터 맨 끝 글자까지를 의미하고, j는 움직이는 pi? 직접 움직이면서 같으면 늘리고 다르면 줄이는 느낌이다.
이걸보고 이해하는 사람이 있을까..
이해가 안가면 하나하나 따라가보는 방법도 좋다. (A A B A A A 로 이해했다.)
getPi() 함수 코드
char str[2000000];
int pi[2000000];
void getPi() {
int j = 0;
for (int i = 1; i < 2 * N; i++) {
while (j > 0 && str[i] != str[j]) {
j = pi[j - 1];
}
if (str[i] == str[j]) {
pi[i] = ++j;
}
}
}
KMP 알고리즘 함수도 pi를 구하는 함수와 매우 유사하다.
이 문제에서는 주어진 문자열이 둘다 N개의 글자로 이루어져있기 때문에,str과 str2의 문자열이 같아지는 조건을 j == N 으로 설정하여 이 경우에만 cnt를 증가시켰다.
KMP 함수 코드
void KMP() {
int j = 0;
for (int i = 0; i < 2 * N; i++) {
while (j > 0 && str[i] != str2[j]) {
j = pi[j - 1];
}
if (str[i] == str2[j]) {
j++;
if (j == N) {
cnt++;
j = pi[j - 1];
}
}
}
}
#define은 여기서 필요없다
굳이 for문을 한번 더 넣어서 n을 찾은 이유는 str, str2의 문자열이 전부 같을 경우에
str 문자열을 두번 이어붙였기 때문에 한번 더 cnt가 증가하므로 경우를 나눠주었다.굳이 for문을 한번 더 돌지 않아도 될 방법이 있을것같지만 KMP가 너무 힘들었다._.
전체코드
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
using namespace std;
#define initialize(x, y) memset(x, y, sizeof(x));
int N, cnt;
char str[2000000];
char str2[1000000];
int pi[2000000];
int gcd(int a, int b) {
int c;
while (b != 0) {
c = a % b;
a = b;
b = c;
}
return a;
}
void getPi() {
int j = 0;
for (int i = 1; i < 2 * N; i++) {
while (j > 0 && str[i] != str[j]) {
j = pi[j - 1];
}
if (str[i] == str[j]) {
pi[i] = ++j;
}
}
}
void KMP() {
int j = 0;
for (int i = 0; i < 2 * N; i++) {
while (j > 0 && str[i] != str2[j]) {
j = pi[j - 1];
}
if (str[i] == str2[j]) {
j++;
if (j == N) {
cnt++;
j = pi[j - 1];
}
}
}
}
int main() {
char input;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> input;
str[i] = input;
str[i + N] = input;
}
for (int i = 0; i < N; i++) {
cin >> input;
str2[i] = input;
}
getPi();
KMP();
int n = 0;
for (int i = 0; i < N; i++) {
if (str[i] == str2[i]) {
n++;
}
}
if (n == N) {
int g = gcd(cnt - 1, N);
cout << (cnt - 1) / g << "/" << N / g;
}
else {
int g = gcd(cnt, N);
cout << cnt / g << "/" << N / g;
}
}
'ALGORITHM > OTHER' 카테고리의 다른 글
[c++] 14425 문자열 집합 - Trie (트라이), set, unordered_set (0) | 2021.01.24 |
---|---|
2231 분해합, 7568 덩치, 1018 체스판 다시칠하기, 1436 영화감독 숌 (0) | 2021.01.10 |
14621 나만 안되는 연애 (2) | 2021.01.06 |
12789 도키도키 간식드리미 (0) | 2021.01.06 |
11582 치킨 TOP N (2) | 2020.12.26 |