일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 카카오톡API
- 딩코딩코
- Java
- 문자열
- HTTP
- CS지식
- 14510 나무 높이
- 백엔드
- 개발공부
- AWS
- 백엔드 면접지식
- 개발자
- BOJ
- 자바
- LAMBDA
- 빈 라인
- 파이썬
- hELLO 스킨
- 백준
- mac 코드 블럭
- 알고리즘
- 그림자 문제
- 코딩테스트
- it세계의 괴물들
- 인터럽트핸들러
- Roy Fielding
- 비전공자
- CPU의 구성요소
- transiant
- sw적성진단
- Today
- Total
아직은 NULL NULL 합니다
[백준:15829/Python] Hashing 문제 본문
해시함수 란 ?
임의의 길이의 입력을 받아서 고정된 길이의 출력을 내보내는 함수로
자료의 저장과 탐색에 쓰인다.
문제 요약
문제 a ~ z 까지 고유한 번호 1 ~ 26으로 부여해 준다.
입력받은 영어로 된 문자열을 고유한 번호로 바꿔준 뒤, 이 수열을 하나의 정수로 치환하기 위해
모든 수열을 더해준다.
그리고 다 더한 값을 유한한 범위를 갖기 위해 적당히 큰 수 M 으로 나눠준다.
이렇게 나오게 되는 것이 해시값이다.
하지만, 서로 다른 문자열이라도 동일한 해시값을 가질 수 있으며, 이를 해시충돌이라고 한다.
이러한 해시충돌을 줄이기 위해 수열의 각 항마다 고유한 계수를 부여하면 되는데
가장 대표적인 방법이 항의 번호에 해당하는 만큼 특정한 숫자 r을 거듭제곱해서 곱해준 다음 더하는 것이다.
이 문제에서는 r, m 을 지정해 주었고 이들은 서로소인 숫자로 하는 것이 일반적이다.
r 은 26 보다 큰 소수 31, M 은 1234567891 (소수)로 지정해 준다.
문제 풀이
먼저 내가 푼 정답은 공유해보자면, 아래와 같다.
import sys
N = int(input())
L = sys.stdin.readline() # 문자열
# 1 ~26 (인덱스에 +1)
e = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
# 각 항을 더해주는 변수
sum = 0
# 고유한 번호를 더 고유하게 만들어주는 숫자 부여
r = 31
# r과 서로소인 M
M = 1234567891
# 각 하나의 문자열마다 고유한 번호로 바꾼 뒤, 더해주는 반복문 만들기
for i in range(len(L)) :
if(L[i] != '\n') : # i 에 줄바꿈도 넘어가서 제외해주기-> 넘어가는 이유가 readline으로 풀어서이다.
sum += (e.index(L[i])+1) * (r**i) # 고유한 번호 더해주기
# 다 더한 것을 가장 큰 숫자 M으로 나눠주기 (정수로 출력)
# 여기서 % 로 나눠준 이유는 수식에 Mod M 이라고 되어있는데
# 이 Mod 의 의미가 M 으로 나눈 뒤 나머지를 리턴한다는 의미이기 때문이다.
print(sum%M)
index() 함수는 문자열을 인자로 받아서, 해당 문자열 위치의 인덱스를 반환한다.
여기서는 e 에 담긴 a~z 문자를 찾아서, 해당 위치의 인덱스를 반환하는데 이때 인덱스는 기본적으로 0부터 시작하기 때문에 +1을 더하여 고유한 번호 1~26인 것에 맞춰준 것이다.
사실 처음에는 M 을 넣고 계산하지 않아도, ide로 테스트케이스 대로 구현했을 때는 똑같이 정답이 나오길래
그냥 제출을 해보았었다. 왜냐하면 힌트에서도 M의 계산이 따로 없었어서 내가 이해를 잘 못했다고 생각했었다.
아래가 그렇게 제출했던 풀이 이다.
import sys
N = int(input())
L = sys.stdin.readline() # 문자열
# 1 ~26 (인덱스에 +1)
e = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
# 각 항을 더해주는 변수
sum = 0
# 고유한 번호를 더 고유하게 만들어주는 숫자 부여
r = 31
# 각 하나의 문자열마다 고유한 번호로 바꾼 뒤, 더해주는 반복문 만들기
for i in range(len(L)) :
if(L[i] != '\n') : # i 에 줄바꿈도 넘어가서 제외해주기
sum += (e.index(L[i])+1) * (r**i) # 고유한 번호 더해주기
# 다 더한 것을 가장 큰 숫자 M으로 나눠주기 (정수로 출력)
print(sum)
그런데 점수배점이 50점만 부여가 되길래, M 계산을 추가해서 맨 위의 풀이와 같이 제출했더니 100점으로 부여가 됐다.
원래 M 같은 소수의 큰 숫자로 나누는 이유가 유한한 범위를 지정해 주기 위해서인데,
M으로 나누지 않아서 무한한 범위를 갖게 돼서 그랬던 것 같다.
추가적으로 e 에 저렇게 하나하나 a~z까지 적는 방식보다 더 간단한 방식이 있는 것 같아서 알아보니
sum += (ord(L[i])-96)*(r**i)
# a 가 ASCII 코드로 97 이어서 -96 으로 계산을 하는 게 더 간단하다.
a의 ASCII 코드가 97이고 z의 ASCII 코드가 122 인 것을 생각하니 96을 빼면 되는 것이었다.
이 해시함수 형식이 자주 쓰인다고 하니 기억해두는 게 좋을 것 같아서 기록해 본다.
'Algorithm > BOJ & SWEA' 카테고리의 다른 글
[BOJ: 7785/ JAVA] 회사에 있는 사람 (0) | 2025.08.24 |
---|---|
[SWEA:14510/JAVA] 나무 높이 (2) | 2025.08.24 |
[백준 : JAVA] 1439. 뒤집기 (2) | 2025.03.08 |
[백준:2903/Python] 중앙 이동 알고리즘 (0) | 2023.11.02 |
[백준:2745/Python] 진법 변환 문제 (2) | 2023.10.30 |