DS Certi (Pro) Linked List , Hash (실전 문제) - jjin-choi/study_note GitHub Wiki
- hcnt = 0 뿐만 아니라 테이블 자체를 초기화 해주어야 함.
-
hcnt = 0;
-
0 ~ size -1 까지 hash[i] = NULL;
-
따라서 size 를 적당히 너무 크지 않게 잡아주어야 함.
-
혹은 사용한 hash index 별도 배열에 기록 (ex. int useHashIdx[])
-
사용한 index만 초기화
-
-
길이는 60,000 이하.
-
hash 문제가 나올 때에는 부수적인 이유로 나옴. 주된 것은 linked list 혹은 다른 것이고 indexing 이나 문자열 일 때 hash ...
-
패턴찾기 문제가 hash 문제..
-
hash 문제 format
- hash key 설정
. A C G T 각 자리 수에 몇 개가 있는지를 넣자.
. ex) AGC : 1110 / GAG : 1020
. 이를 CODE 로 어떻게 변경할까 ? (진법 / 각 네 수의 합 - 겹치는 수가 너무 많아짐. / 각 네 수의 제곱의 합 - 마찬가지..)
. 자리수가 의미있기 때문에 진법 !
. 한 자리에 나올 수 있는 최대 숫자가 1000 이므로 (문제에서 주어진 길이 k 가 1000) k 를 1001 진법으로.. . 33 진법으로 해도 되긴 함. 충돌 처리를 잘 하면 되기 때문에 - collision 처리 (chaining)
- hash table size 설정 (chaining 시 data 수 ^ 2) . DATA 수는 6만개. 따라서 SIZE 는 2^16 = 65,XXX 정도 . MASK = SIZE - 1
- hash function 설정 . hidx = CODE & MASK
- hash key 설정
. A C G T 각 자리 수에 몇 개가 있는지를 넣자.
. ex) AGC : 1110 / GAG : 1020
. 이를 CODE 로 어떻게 변경할까 ? (진법 / 각 네 수의 합 - 겹치는 수가 너무 많아짐. / 각 네 수의 제곱의 합 - 마찬가지..)
. 자리수가 의미있기 때문에 진법 !
-
오답노트
- probing 을 할 때에 return 이 없을때 hcnt[hidx] 에 할당해주고 cnt++ 에 주의.. !
#include <stdio.h>
typedef unsigned long long ull;
const int MAX_K = (1e3 + 1);
const int MAX_N = (6e5 + 5);
const int SIZE = 1 << 16;
const int MODE = SIZE - 1;
int N, K, hcnt, max = 0;
char str[MAX_N];
int cnt[4];
struct _hash {
ull code;
int cnt;
_hash* next;
_hash* alloc(ull _code, _hash *_next) {
code = _code, next = _next;
return this;
}
} hbuff[MAX_N], *hash[SIZE]; // hash 는 SIZE 만큼 .. !
int hash_func(int mode) {
ull CODE = 0;
for (int i = 0; i < 4; i++)
CODE = CODE * MAX_K + cnt[i];
if (mode) return CODE;
else return CODE & MODE;
}
_hash* probing() {
int hidx = hash_func(0);
int code = hash_func(1);
for (_hash* p = hash[hidx]; p; p = p->next)
if (p->code == code) return p;
return 0;
}
void update_cnt(char c, int flag) {
if (c == 'A') cnt[0] += flag;
if (c == 'C') cnt[1] += flag;
if (c == 'G') cnt[2] += flag;
if (c == 'T') cnt[3] += flag;
}
int main()
{
freopen("input.txt", "r", stdin);
scanf("%d %s", &K, str);
for (int i = 0; str[i]; i++) {
update_cnt(str[i], 1);
if (K <= i) update_cnt(str[i - K], -1);
if (K - 1 <= i) {
_hash *p = probing();
if (!p) {
int hidx = hash_func(0);
int code = hash_func(1);
hash[hidx] = hbuff[hcnt++].alloc(code, hash[hidx]);
hash[hidx]->cnt++;
if (max < hash[hidx]->cnt) max = hash[hidx]->cnt;
}
else {
p->cnt++;
if (max < p->cnt) max = p->cnt;
}
}
}
printf("%d\n", max);
return 0;
}
-
hash key
- 길이 k인 숫자 배열
-
hash function
- 배열 길이가 최대 1000 이고, CODE 가 실제 유효한 값은 아님.
- djb2 : seed 5381, 33 진법
- 편법.. : DATA 수에 비해 SIZE 가 충분할 경우에는 제곱의 합
-
hash table size
- DATA 수 1000개, SIZE 2^12, MASK = SIZE -1
-
오답노트
- 1 프로그램에 한 번 더해진 것은 다시 더해지지 않도록 처리하기 !
#include <stdio.h>
typedef unsigned long long ull;
const int MAX = 1000 + 5;
const int SIZE = 1 << 16;
const int MODE = SIZE - 1;
int N, K, M, hcnt;
int first[MAX], in[MAX];
struct _hash {
int base;
int cnt = 0; // 같은 프로그램 내에서는 더하면 안됨...
_hash* next;
_hash* alloc(int _base, _hash* _next) {
base = _base, next = _next;
return this;
}
} hbuff[MAX], *hash[SIZE];
bool is_match(int base, int arr[]) {
int l_check = 1, r_check = 1;
for (int k = 0; k < K; k++) {
if (first[base + k] != arr[k]) {
l_check = 0; break;
}
}
for (int k = 0; k < K; k++) {
if (first[base + k] != arr[K - k - 1]) {
r_check = 0; break;
}
}
if (l_check || r_check) return true;
return false;
}
int hash_func(int base) {
ull CODE = (in[base] * in[base] + in[base - K + 1] * in[base - K + 1]);
return CODE & MODE;
}
_hash* probing(int n, int base, int arr[]) {
int hidx = hash_func(base);
for (_hash* p = hash[hidx]; p; p = p->next) {
if (is_match(p->base, arr))
return p;
}
return 0;
}
void make_hash_table(int M) {
for (int m = K - 1 ; m < M ; m++) {
int hidx = hash_func(m);
_hash *p = probing(0, m, first + m - K + 1);
if (!p) {
hash[hidx] = hbuff[hcnt++].alloc(m - K + 1, hash[hidx]);
hash[hidx]->cnt++;
}
}
}
int check_virus(int n, int M) {
int flag = 0;
for (int m = K - 1; m < M ; m++) {
int hidx = hash_func(m);
_hash *p = probing(n, m, in + m - K + 1);
if (p && p->cnt == n && flag != hidx) {
p->cnt++;
flag = hidx;
}
}
return flag;
}
int main()
{
freopen("input.txt", "r", stdin);
scanf("%d %d", &N, &K);
for (int n = 0; n < N; n++) {
scanf("%d", &M);
for (int m = 0; m < M; m++) {
scanf("%d", in + m);
if (n == 0) first[m] = in[m];
}
if (n == 0) make_hash_table(M);
else
if (!check_virus(n, M)) {
printf("NO\n");
return 0;
}
}
printf("YES\n");
return 0;
}
-
오답노트
- null 참조 시 Runtime Error:Segmentation fault 발생 !!
- 문제 잘 읽기.. :) 특히 조건문 !!! 주의하기 (로그인한 ! 경우에만 로그아웃)
- 음수가 나왔다는 건 뭔가 잘못된거다.. :)
#include <stdio.h>
const int MAX = (int)(5e5 + 5);
const int LEN = 15;
const int SIZE = 1 << 19;
const int MODE = SIZE - 1;
int N, cmd, hcnt, scnt, lcnt;
int strcmp(char*s, char*t) {
while (*s && *s == *t) ++s, ++t;
return *s - *t;
}
void strcpy(char*dest, const char*src) {
while ((*dest++ = *src++));
}
struct _hash {
char id[LEN];
int is_login; // 로그인 시 1
int is_close; // 탈퇴 시 1
_hash* next;
_hash* alloc(char _id[LEN], _hash* _next) {
strcpy(id, _id), next = _next;
return this;
}
} hbuff[MAX], *hash[SIZE], *p;
int hash_func(char KEY[LEN]) {
int CODE = 0;
for (int i = 0; KEY[i]; i++)
CODE = CODE * 37 + KEY[i];
return CODE & MODE;
}
_hash* probing(char* id) {
int hidx = hash_func(id);
for (_hash* pp = hash[hidx]; pp; pp = pp->next)
if (!strcmp(pp->id, id)) return pp;
return 0;
}
int validate(char* id) {
if (p && !(p->is_close)) return 1;
return 0;
}
int activate(char* id) {
if (validate(id)) return p->is_login;
return 0;
}
int signup(char* id) {
if (p && p->is_close) {
p->is_close = 0;
scnt++;
}
if (!p) {
int hidx = hash_func(id);
hash[hidx] = hbuff[hcnt++].alloc(id, hash[hidx]);
scnt++;
}
return scnt;
}
int close(char* id) {
if (validate(id)) {
p->is_close = 1; scnt--;
if (p->is_login) { p->is_login = 0; lcnt--; }
}
return scnt;
}
int login(char* id) {
if (validate(id) && !(p->is_login)) { p->is_login = 1; lcnt++; }
return lcnt;
}
int logout(char* id) {
if (validate(id) && p->is_login) { p->is_login = 0; lcnt--; }
return lcnt;
}
int main()
{
freopen("input.txt", "r", stdin);
scanf("%d", &N);
for (int n = 0; n < N; n++) {
char in[LEN];
scanf("%d %s", &cmd, in);
p = probing(in);
if (cmd == 1) {
int ret = validate(in);
printf("%d\n", ret);
}
else if (cmd == 2) {
int ret = activate(in);
printf("%d\n", ret);
}
else if (cmd == 3) {
int ret = signup(in);
printf("%d\n", ret);
}
else if (cmd == 4) {
int ret = close(in);
printf("%d\n", ret);
}
else if (cmd == 5) {
int ret = login(in);
printf("%d\n", ret);
}
else if (cmd == 6) {
int ret = logout(in);
printf("%d\n", ret);
}
}
return 0;
}
-
오답노트
- hash size : data 수가 1000 * 1000 이므로 그 이상.. 1 << 20
- hash key : M * M 상태
- hash function : . M * M 값을 2진법으로 표현하여 code 생성 . M * M 앞쪽 4*5 값을 2진법으로 표현하여 code 생성 (-> 0 ~ 1 << 20, 즉 code 값이 hash index)
- 배열 값을 and 연산 으로 해서 필요한 부분만 골라내보기
- 굳이 다 할 필요 없이 45만으로도 비교해도 충분.. (나올 수 있는 경우 (2^(45)) 의 수가 데이터 수 (1000 * 1000) 만큼 되기 때문에 이상적임)
- B 배열 크기 설정 잘 하자. hash pointer p 가 이상한 값을 가리킴
- 디버깅 시에는 ctrl + F10 을 !
-
풀이
- init() : 모든 A 의 시작점에서의 code 값을 계산해서 hash table 에 상태 저장
- query() : B 의 code -> hidx 값을 계산해서 hash table[hidx] probing 통해 같은지 확인.
const int MAX = (int)(1e3 + 3);
const int MIN_R = 4;
const int MIN_C = 5;
const int SIZE = 1 << 20;
const int MODE = SIZE - 1;
int N, M, A[MAX][MAX], B[24][24], hcnt;
struct _hash {
int r, c;
_hash* next;
_hash* alloc(int _r, int _c, _hash* _next) {
r = _r, c = _c, next = _next;
return this;
}
} hbuff[MAX * MAX], *hash[SIZE];
int hash_func(int R, int C, int mode) {
int ret = 0;
for (int r = 0; r < MIN_R; r++)
for (int c = 0; c < MIN_C; c++) {
if (mode == 'A') ret = (ret << 1) + A[r + R][c + C];
if (mode == 'B') ret = (ret << 1) + B[r][c];
}
return ret & MODE;
}
void init(int n, int ap[][1000], int m) {
::N = n, ::M = m;
hcnt = 0;
// initialize hash table
for (int i = 0; i < SIZE; i++) hash[i] = 0;
// copy ap to A array
for (int r = 0; r < N; r++)
for (int c = 0; c < N; c++)
A[r][c] = ap[r][c];
// push pattern to hash table
for (int r = 0; r < N - M + 1; r++)
for (int c = 0; c < N - M + 1; c++) {
int hidx = hash_func(r, c, 'A');
hash[hidx] = hbuff[hcnt++].alloc(r, c, hash[hidx]);
}
}
bool compare_pattern(int R, int C) {
for (int r = 0; r < M; r++)
for (int c = 0; c < M; c++)
if (A[r + R][c + C] != B[r][c]) return 0;
return 1;
}
int query(int bp[][20]) {
// copy bp to B array
for (int r = 0; r < M; r++)
for (int c = 0; c < M; c++)
B[r][c] = bp[r][c];
// find pattern
int hidx = hash_func(0, 0, 'B');
for (_hash* p = hash[hidx]; p; p = p->next)
if (compare_pattern(p->r, p->c))
return p->r * N + p->c;
return 0;
}