Daily Algorithm - Day 32

105·2025년 1월 22일
0

Daily Algorithm

목록 보기
33/34

해시 해킹

그린닷컴의 운영자 연두는 비밀번호를 평문 그대로 저장하는 과오를 뒤로하고, 이제부터 암호에 해시 함수를 적용해 저장하려고 한다.
연두가 아는 해시 함수라고는 알고리즘 문제 풀이에 많이 사용되는 롤링 해시 함수밖에 없기 때문에 이것을 응용하여 사용하기로 했다.

그린닷컴의 비밀번호 규칙은 꽤 특이한데, 길이가 정확히 NN이어야 하며, 비밀번호를 이루는 문자는 지정된 MM개의 문자 중 하나여야 한다.
따라서, 사용 가능한 각 문자를 00부터 차례대로 정수에 대응시키면, 비밀번호를 길이가 N이고 모든 원소가 00 이상 M1M-1 이하인 배열

P=[P0,P1,...,PN1]P = [P_0, P_1, ..., P_{N-1}]로 나타낼 수 있다.
이렇게 비밀번호를 배열 P로 나타낸 후, 미리 정해진 정수 A를 이용하여 다음과 같은 해시 함수 h를 적용한다.

h(P)=(P0×A0+P1×A1+...+PN1×AN1)modMh(P) = (P_0 \times A^0 + P_1 \times A^1 + ... + P_{N-1} \times A^{N-1}) mod M 

예를 들어 배열
P=[10,30,20],A=7,M=55P = [10, 30, 20], A = 7, M = 55인 경우를 생각해보자. 이 경우
h(P)=(10×70+30×71+20×72)mod55=(10+210+980)mod55=45h(P) = (10 \times 7^0 + 30 \times 7^1 + 20 \times 7^2) mod 55 = (10 + 210 + 980) mod 55 = 45이다.

여기서 modmod는 나머지 연산으로 1200=21×55+451200 = 21 \times 55 + 45이므로 1200mod55=451200 mod 55 = 45이다.
따라서 해시값은 항상 00 이상 M1M-1 이하의 정수이다.

그린닷컴 관리자 계정의 비밀번호 해시값을 해킹한 재현이는, 이 해시값으로 실제 비밀번호가 뭐였는지 역추적해보려고 한다.
하지만 그린닷컴에서 사용 가능한 비밀번호는 MNM^N개나 있고, 이 중 과연 알아낸 해시값과 일치하는 비밀번호는 몇 개나 될지 궁금해졌다. 여러분이 이것을 대신 구해주자.

입력

첫째 줄에 비밀번호의 길이 NN과 문자 종류의 개수 MM, 정수 AA가 주어진다.
(1N,M,A5,000,000)(1 ≤ N, M, A ≤ 5,000,000)

둘째 줄에 재현이가 알아낸 해시값 정수 HH가 주어진다. (0H<M)(0 ≤ H < M)

출력

주어진 해시값을 갖는 비밀번호의 개수를 출력한다. 출력하는 값이 너무 커질 수 있으므로, 이것을 1,000,000,007(=109+7)1,000,000,007 ( = 10^9 + 7)로 나눈 나머지를 출력한다.

초반 1시간 정도는 해싱함수라는 키워드에 매몰되어서 해시맵을 이용해 풀이를 해보려 했으나 시간 초과가 발생했다. 해싱으로는 도저히 1초 안에 큰 숫자를 계산할 수 없어서 문제를 잘 살펴보니 다음과 같은 점을 발견했다.

  • 주어진 수식에서 P0P_0가 될 수 있는 범위는 0~M-1이다.
  • 따라서 P_0을 제외한 수가 고정되어있다고 할 경우 주어진 해시값을 만족하는 비밀번호는 1개이다.
  • P_0을 제외한 수의 조합은 MN1M^{N-1}개이다.
  • MN1M^{N-1}개의 조합에서 조합 당 하나의 숫자만 주어진 해시값을 만족한다.
  • 따라서 어떤 해시값이 주어지더라도, MN1M^{N-1}을 리턴하기만 하면 된다.
  • 단, 숫자가 너무 커지면 계산이 오래 걸리니 반복문을 통해 항상 나머지를 계산해주자.
import sys

MOD = 1_000_000_007

def count_passwords(N, M, A, H):
    answer = 1
    
    for i in range(N-1):
        answer *= M
        answer %= MOD # 오버플로우 방지를 위해 나머지를 미리 계산
        
    return answer

# 입력 처리
N, M, A = map(int, sys.stdin.readline().split())
H = int(sys.stdin.readline().strip())

# 결과 출력
print(count_passwords(N, M, A, H))


>>>
5000000 5000000 5000000
1
73352076   # correct

오늘은 여기까지
-2025.01.23-

ps. 내일부터 해외여행으로 인해 Daily Algorithm은 2주 중단 후 2025.02.11에 다시 재개될 예정이다.

profile
focus on backend

0개의 댓글

관련 채용 정보