해당 문제는 Greedy 문제로 반복문(for) 통해 답을 구해나가는 문제이다.
1. 문제 설명
여러 언론사에서 쏟아지는 뉴스, 특히 속보성 뉴스를 보면 비슷비슷한 제목의 기사가 많아 정작 필요한 기사를 찾기가 어렵다. Daum 뉴스의 개발 업무를 맡게 된 신입사원 튜브는 사용자들이 편리하게 다양한 뉴스를 찾아볼 수 있도록 문제점을 개선하는 업무를 맡게 되었다.
개발의 방향을 잡기 위해 튜브는 우선 최근 화제가 되고 있는 "카카오 신입 개발자 공채" 관련 기사를 검색해보았다.
- 카카오 첫 공채..'블라인드' 방식 채용
- 카카오, 합병 후 첫 공채.. 블라인드 전형으로 개발자 채용
- 카카오, 블라인드 전형으로 신입 개발자 공채
- 카카오 공채, 신입 개발자 코딩 능력만 본다
- 카카오, 신입 공채.. "코딩 실력만 본다"
- 카카오 "코딩 능력만으로 2018 신입 개발자 뽑는다"
기사의 제목을 기준으로 "블라인드 전형"에 주목하는 기사와 "코딩 테스트"에 주목하는 기사로 나뉘는 걸 발견했다. 튜브는 이들을 각각 묶어서 보여주면 카카오 공채 관련 기사를 찾아보는 사용자에게 유용할 듯싶었다.
유사한 기사를 묶는 기준을 정하기 위해서 논문과 자료를 조사하던 튜브는 "자카드 유사도"라는 방법을 찾아냈다.
자카드 유사도는 집합 간의 유사도를 검사하는 여러 방법 중의 하나로 알려져 있다. 두 집합 A, B 사이의 자카드 유사도 J(A, B)는 두 집합의 교집합 크기를 두 집합의 합집합 크기로 나눈 값으로 정의된다.
예를 들어 집합 A = {1, 2, 3}, 집합 B = {2, 3, 4}라고 할 때, 교집합 A ∩ B = {2, 3}, 합집합 A ∪ B = {1, 2, 3, 4}이 되므로, 집합 A, B 사이의 자카드 유사도 J(A, B) = 2/4 = 0.5가 된다. 집합 A와 집합 B가 모두 공집합일 경우에는 나눗셈이 정의되지 않으니 따로 J(A, B) = 1로 정의한다.
자카드 유사도는 원소의 중복을 허용하는 다중집합에 대해서 확장할 수 있다. 다중집합 A는 원소 "1"을 3개 가지고 있고, 다중집합 B는 원소 "1"을 5개 가지고 있다고 하자. 이 다중집합의 교집합 A ∩ B는 원소 "1"을 min(3, 5)인 3개, 합집합 A ∪ B는 원소 "1"을 max(3, 5)인 5개 가지게 된다. 다중집합 A = {1, 1, 2, 2, 3}, 다중집합 B = {1, 2, 2, 4, 5}라고 하면, 교집합 A ∩ B = {1, 2, 2}, 합집합 A ∪ B = {1, 1, 2, 2, 3, 4, 5}가 되므로, 자카드 유사도 J(A, B) = 3/7, 약 0.42가 된다.
이를 이용하여 문자열 사이의 유사도를 계산하는데 이용할 수 있다. 문자열 "FRANCE"와 "FRENCH"가 주어졌을 때, 이를 두 글자씩 끊어서 다중집합을 만들 수 있다. 각각 {FR, RA, AN, NC, CE}, {FR, RE, EN, NC, CH}가 되며, 교집합은 {FR, NC}, 합집합은 {FR, RA, AN, NC, CE, RE, EN, CH}가 되므로, 두 문자열 사이의 자카드 유사도 J("FRANCE", "FRENCH") = 2/8 = 0.25가 된다.
2. 입출력 예
3. 문제 풀이
Map 객체를 만들어 key값으로 문자열을 두고 하나씩 개수를 세는 방법으로
function solution(str1, str2) {
var answer = 0;
// 1) 교집합 개수
let itrt = 0;
// 2) 합집합 개수
let uno = 0;
// 3) 비교를 위해 소문자로 맞춰 배열로 변환한다.
str1 = str1.toLowerCase().split('');
str2 = str2.toLowerCase().split('');
// 4) key(문자), value(개수)를 담은 Map 객체를 return해주는 함수
const mapStr1 = createMap(str1);
const mapStr2 = createMap(str2);
// 11) mapStr1 객체에 담긴 개수 만큼 반복
for (let [key, val] of mapStr1) {
// 12) mapStr2 객체에도 해당 문자를 가지고 있을 경우만
if(mapStr2.has(key)) {
// 13) 교집합은 최소 값의 개수를 더한다.
itrt += Math.min(mapStr2.get(key), val);
// 14) 합집합은 최대 값의 개수를 더한다.
uno += Math.max(mapStr2.get(key), val);
} else {
// 15) mapStr1 객체에만 있는 값을 합집합의 개수에 더한다.
uno += val;
}
}
for (let [key, val] of mapStr2) {
// 16) mapStr2 객체에만 있는 값을 합집한의 개수에 더한다.
if(!mapStr1.has(key)) uno += val;
}
// 17) 교집합 / 합집합 * 65336 의 값에서 소수점을 버린다.
answer = Math.floor(itrt / uno * 65536);
// 18) 공집합일 경우 1로 정의하기 때문에 true일 경우 65536을 아니면 answer을 return한다.
return isNaN(answer) ? 65536 : answer;
}
// 5) Map 함수
function createMap(arrStr){
const mapStr = new Map()
// 6) 인자로 받은 문자 배열 만큼 반복문을 돌린다.
arrStr.forEach((str,i) => {
// 7) 두자리씩 자르기 때문에 i+1의 값이 있을 경우만
if(i+1 < arrStr.length) {
// 8) 문자 배열 i, i+1이 전부 소문자일 경우만
if((/[a-z]/).test(str) && (/[a-z]/).test(arrStr[i+1])) {
// 9) 두자리 문자로 만든다. (key)
str = (str + arrStr[i+1]);
// 10) key값으로 가지고있으면, 기존 값에서 +1, 없으면 1를 담는다.
mapStr.has(str) ? mapStr.set(str, mapStr.get(str) + 1) : mapStr.set(str, 1)
}
}
});
// 11) Map 객체를 return한다.
return mapStr;
}
'Algorithm' 카테고리의 다른 글
[프로그래머스/JavaScript] Lv.2 롤케이크 자르기 (0) | 2022.12.22 |
---|---|
[프로그래머스/JavaScript] Lv.1 햄버거 만들기 (0) | 2022.12.21 |
[프로그래머스/JavaScript] Lv.1 푸드 파이트 대회 (6) | 2022.12.16 |
[프로그래머스/JavaScript] Lv.3 숫자 타자 대회 (1) | 2022.12.15 |
[프로그래머스/JavaScript] Lv.1 과일 장수 (0) | 2022.12.14 |