#해시 #모듈러연산
백준_브론즈2_Hashing
정.말.쉽.지.않.았.다.
삽.질.도.많.이.했.고.이.해.하.는.데.에.오.래.걸.렸.다.
아래 코드 볼 필요도 없다.
이번 주차 주제가 해시니까 해시로 풀면 되겠지~ 하고 해시로 풀었다.
문자열의 각 문자를 key로 지정하고 그에 해당되는 계산 값을 value에 저장하고 반복문으로 value들을 더하려고 했다.
그런데 map은 key를 중복할 수 없기 때문에 연속으로 같은 문자를 가지는 예제 2번에 벗어난다.
그리고 BufferedWriter는 문자열로 반환하기 때문에 int를 String으로 변환해주는 과정이 필요하다.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
HashMap<Character, Integer> map = new HashMap<>();
int rtn = 0;
// 문자열의 길이
int l = Integer.parseInt(br.readLine());
// 길이만큼 입력된 문자열
String s = br.readLine();
// 해시맵 저장
for(int i=0; i<l; i++) {
// 소문자 a의 아스키 값은 97
map.put(s.charAt(i), s.charAt(i)-96);
}
// map 반복문으로 계산하여 각 문자의 계산 결과를 map에 저장
for(Map.Entry<Character, Integer> m : map.entrySet()) {
map.put(m.getKey(), m.getValue() * (int)Math.pow(31, m.getValue()-1));
}
// map 반복문으로 더하기
for(Map.Entry<Character, Integer> m : map.entrySet()) {
rtn += m.getValue();
}
bw.write(rtn);
bw.flush();
bw.close();
br.close();
}
}
첫번째 도전에서 실패한 테스트 케이스가 있기에 해시로 푸는 것은 아닌 것 같다고 생각했다.
그러면 그냥 계산한 값을 더하면 되겠구나 생각했다.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int rtn = 0;
// 문자열의 길이
int l = Integer.parseInt(br.readLine());
// 길이만큼 입력된 문자열
String s = br.readLine();
// 해시 값 저장
for(int i=0; i<l; i++) {
// 소문자 a의 아스키 값은 97 (97-96=1)
int charToInt = s.charAt(i)-96;
rtn += charToInt * Math.pow(31, i);
}
bw.write(String.valueOf(rtn));
bw.flush();
bw.close();
br.close();
}
}
이리저리 해도 계속 50점이라서 문제에 나와있는 것을 모두 활용해야 되는구나 생각했다.
(각 문자 * 31^각 문자-1) 계산한 후 mod M 적용하는 것이라 생각했다.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
final int r = 31;
final int disjoint = 1234567891;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int squared = 1; // 제곱 계산 변수
int rtn = 0; // 결과 저장 변수
// 문자열의 길이
int l = Integer.parseInt(br.readLine());
// 길이만큼 입력된 문자열
String s = br.readLine();
// 해시 값 저장
for(int i=0; i<l; i++) {
// 소문자 a의 아스키 값은 97 (97-96=1)
int charToInt = s.charAt(i)-96;
rtn += (charToInt * squared) % disjoint;
// 다음 제곱값 계산
squared *= r;
}
bw.write(String.valueOf(rtn));
bw.flush();
bw.close();
br.close();
}
}
세번째도 50점? 나 100점 받고 싶은데?!
결국 chatGPT의 도움을 받았다.
모듈러 연산에 대한 개념과 해시 함수와 모듈러 연산의 관계을 잘 알아야 풀 수 있는 문제였다.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
final int r = 31;
final int disjoint = 1234567891;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
long squared = 1; // 제곱 계산 변수
long rtn = 0; // 결과 저장 변수
// 문자열의 길이
int l = Integer.parseInt(br.readLine());
// 길이만큼 입력된 문자열
String s = br.readLine();
// 해시 값 저장
for(int i=0; i<l; i++) {
// 소문자 a의 아스키 값은 97 (97-96=1)
int charToInt = s.charAt(i)-96;
rtn = (rtn + (charToInt * squared) % disjoint) % disjoint;
// 다음 제곱값 계산
squared = (squared * r) % disjoint;
}
bw.write(String.valueOf(rtn));
bw.flush();
bw.close();
br.close();
}
}
내가 생각하는 수식의 내용은 이러했다.
내용 복습 겸 아래의 모듈러 연산에 대해 공부한 내용 중 덧셈, 곱셈 부분을 적용하여 다시 풀어봤다.
그래봤자 모듈러 연산 부분 코드 조금 바뀐 거지만..
chatGPT가 준 코드를 봤을 때 그냥 무지성 모듈러 연산 한거 아닌가 했는데 알고 보니 이유가 있었던 것 같다.
rtn과 charToInt는 숫자가 범위 안에 속하니 모듈러 연산을 일부러 안해준 것 같다.
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
final int r = 31;
final int disjoint = 1234567891;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
long squared = 1; // 제곱 계산 변수
long rtn = 0; // 결과 저장 변수
// 문자열의 길이
int l = Integer.parseInt(br.readLine());
// 길이만큼 입력된 문자열
String s = br.readLine();
// 해시 값 저장
for(int i=0; i<l; i++) {
// 소문자 a의 아스키 값은 97 (97-96=1)
int charToInt = s.charAt(i)-96;
rtn = (rtn % disjoint + ((charToInt % disjoint) * squared) % disjoint) % disjoint;
// 다음 제곱값 계산
squared = (squared * r) % disjoint;
}
bw.write(String.valueOf(rtn));
bw.flush();
bw.close();
br.close();
}
}
보통 a mod n 또는 a%n 으로 표현
결과는 a를 n으로 나눈 나머지
해시 함수는 입력 값을 어떠한 숫자로 변환
해시 테이블의 크기가 N이라고 할 때 (입력값 mod N) 을 계산하면 해시값이 항상 0부터 N-1 사이의 값으로 유지
해시 테이블의 배열 인덱스를 초과하지 않도록 보장
모듈러 연산은 키를 테이블 크기 내에 균일하게 분산시킴
즉 입력값들이 특정 영역에 몰리지 않도록 해줌
해시 함수는 두 입력값이 동일한 해시값을 갖는 경우가 발생할 수 있음
모듈러 연산을 사용하면 충돌의 분포가 예측 가능해지고 충돌 처리(체이닝, 선형 탐색)가 쉬워짐
모듈러 연산은 해시 함수의 계산 속도를 저하시키지 않음
간단한 수학 연산이기 때문