post image post image post image post image post image post image post image post image

https://www.acmicpc.net/problem/1593

 

1593번: 문자 해독

첫째 줄에 고고학자들이 찾고자 하는 단어 W의 길이 g와 발굴된 벽화에서 추출한 문자열 S의 길이 |S|가 빈 칸을 사이에 두고 주어진다. (1≤g≤3000,  g≤|S|≤3,000,000) 둘째 줄에 W, 셋째 줄에 S의 실

www.acmicpc.net

 

슬라이딩 윈도우 알고리즘으로 해결하는 문제다.

문자열을 탐색하는 문제라 처음에는 라빈카프 알고리즘으로 순서를 상관하지 않고 시간 내에 문제를 해결할 수 없을까 고민하다가 해당 캐릭터의 아스키코드 값의 제곱을 더해서 해싱하는 방법을 떠올렸는데, 이렇게 할 시 너무 쉽게 해쉬 충돌이 발생한다.

 

예를 들어 "awr" 과 "zcm"은 시그마 제곱으로 해싱할 시 해쉬 값이 같다.

hash("awr") = (97 ^ 2) + (119 ^ 2) + (114 ^ 2) = 9409 + 14161 + 12996 = 36,566

hash("zcm") = (122 ^ 2) + (99 ^ 2) + (109 ^ 2) = 14884 + 9801 + 11881 = 36,566

 

따라서 정답이 되는 경우가 아닌데도 정답을 체크해서 틀리게 된다. 참고로 약간 오기가 생겨서 세제곱, 네제곱도 제출해봤는데 세제곱은 마찬가지로 해쉬 충돌로 오답 처리가 되고 네제곱으로 해싱할 시 정답 처리가 되었다. ㅋㅋㅋㅋㅋ

 

그래도 정해는 아니니 지금부터 설명할 정해로 풀도록 하자.

 

정해는 슬라이딩 윈도우 알고리즘으로, 한 칸씩 이동하면서, 빠지는 문자가 찾는 문자열에 매칭되는 문자였는지, 들어오는 문자가 찾는 문자열에 매칭되는 문자인지 확인해서 그 개수를 세어주면 된다.

 

#include <iostream>
#include <string>

using namespace std;

void solution() {
	int g, n;
	int cnt = 0, ans = 0;
	char cur;
	string s, w;

	//각각 w와 윈도우 안의 s에 있는 각각의 캐릭터의 개수를 저장할 배열
	int char_w[128] = { 0, }, char_s[128] = { 0, };

	cin >> g >> n >> w >> s;
	for (int i = 0; i < g; i++) {
		++char_w[w[i]];
	}
    //윈도우 안에 첫 g개의 문자를 삽입
	for (int i = 0; i < g; i++) {
		cur = s[i];
		++char_s[cur];
        //char_s가 char_w보다 작거나 같은 동안은 매칭되는 문자이므로 ++cnt
		if (char_s[cur] <= char_w[cur]) ++cnt;
	}

	//만약 매칭되는 문자의 수가 g와 같다면 정답 개수 증가
	if (cnt >= g) ++ans;
	for (int i = g; i < n; i++) {
    	//앞에서 문자 하나 제거
		cur = s[i - g];
        //여기서 char_w[cur]이 0이 아닌지 확인하는 부분은 없어도 동작한다.
		if (char_w[cur] && char_s[cur] <= char_w[cur]) --cnt;
		--char_s[cur];

		//뒤에 문자 하나 추가
		cur = s[i];
		++char_s[cur];
		if (char_w[cur] && char_s[cur] <= char_w[cur]) ++cnt;
		if (cnt >= g) ++ans;
	}

	cout << ans;
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	solution();

	return 0;
}
메모리: 7544 kb 시간: 32 ms

 

그리고 참고용으로 해싱으로 푼 코드도 올린다. 시간 차이가 매우 많이 나는 것을 확인할 수 있다.

#include <iostream>
#include <string>
#include <cmath>

using namespace std;
using ll = long long;

void solution() {
	int g, n;
	ll ans = 0, hash_w = 0, hash_s = 0;
	string s, w;
	int pow_n = 4;
    
	cin >> g >> n >> w >> s;
	for (int i = 0; i < g; i++) {
		hash_w += pow(w[i], pow_n);
		hash_s += pow(s[i], pow_n);
	}

	if (hash_w == hash_s) ++ans;
	for (int i = g; i < n; i++) {
		hash_s -= pow(s[i - g], pow_n);
		hash_s += pow(s[i], pow_n);
		if (hash_w == hash_s) ++ans;
	}
	
	cout << ans;
}

int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	solution();

	return 0;
}
메모리: 7732 kb 시간: 412 ms

'컴퓨터 사이언스 > 1고리즘' 카테고리의 다른 글

백준 19237 : 어른 상어  (0) 2021.08.09
백준 14921 : 용액 합성하기  (0) 2021.08.08
백준 2212 : 센서  (0) 2021.08.06
백준 10775 : 공항  (0) 2021.08.06
백준 1701 : Cubeditor  (0) 2021.08.05

+ Recent posts