알고리즘

프로그래머스JS | [1차] 뉴스 클러스터링

3jun 2022. 7. 29. 16:32
출처 : 프로그래머스 - 코딩테스트 연습 - 2018 KAKAO BLIND RECRUITMENT- [1차] 뉴스 클러스터링

❌ Solution ( 정확성 53.8 / 100 )

function solution(str1, str2) {
    var answer = 0;
    
    const upperStr1 = str1.toUpperCase();
    const upperStr2 = str2.toUpperCase();
    
    const arrStr1 = splitEl(upperStr1);
    const arrStr2 = splitEl(upperStr2);
    
    const union = [];
    const intersection = [];
    
    
    for(let i = 0; i < arrStr1.length; i++) {
        // 변환된 str1 배열의 인자가 변환된 str2 배열에도 존재하고, 교집합에 들어있지 않다면 교집합에 추가
        if(arrStr2.indexOf(arrStr1[i]) !== -1 && intersection.indexOf(arrStr1[i]) === -1)  {
            intersection.push(arrStr1[i])
        }
        // 합집합 배열에는 변환된 str1 배열을 추가
        if(union.indexOf(arrStr1[i]) === -1) {
            union.push(arrStr1[i]);
        }
        
    }

    for(let j = 0; j < arrStr2.length; j++) {
        // 합집합 배열에 변환된 str2 배열의 인자가 없다면 해당 인자를 합집합 배열에 추가
        if(union.indexOf(arrStr2[j]) === -1) {
            union.push(arrStr2[j])
        }
    }
    
    // 정답 계산식
    answer = parseInt(65536 * (intersection.length / union.length));
    // 두 집합이 공배열일 경우 처리
    if(isNaN(answer)) return 65536;
    return answer;
}

// 문자열을 2글자 단위로 쪼개기 위한 함수
function splitEl(str) {
    const splitedArr = [];
    const reg = /^[A-Z]{2}/g
    
    for(let i = 0; i < str.length -1; i++) {
        const newEl = str.split('')[i] + str.split('')[i+1];
        if(newEl.match(reg) !== null) {
            splitedArr.push(newEl)
        }
    }
    return splitedArr;
}

1. 영문자로 된 글자쌍만 유효하고, 대소문자는 구별하지 않으므로 splitEl 함수를 사용하여 주어진 문자열을 대문자로 통일해준 다음 정규표현식을 사용하여 영문자로 이루어진 다중집합을 return 한다.

2. str1의 다중집합의 원소가 str2의 다중집합에도 들어있고 교집합 배열 intersection 에 포함되어 있지 않다면 해당 원소를 intersection 배열에 추가한다.

3. str1의 다중집합의 원소는 모두 합집합 배열 union 에 추가한다. 

4. str2의 다중집합의 원소 중 union 배열에 들어있지 않은 원소가 있다면 union 배열에 추가한다.

5. 교집합 intersection의 length, 합집합 union 의 length를 사용하여 자카드 유사도를 구하고 65536를 곱한 후에 소수점 아래를 버리고 정수부만 출력한다.

 

💡 자카드 유사도는 원소의 중복을 허용하는 다중집합에 대해 확장할 수 있다. 라는 문구가 있는데, 이를 고려하지 않고 코드를 작성하였다. 

이를 고려한 예제를 확인하면 아래와 같다.

A = { 1, 1, 2, 2, 3 }		// 다중집합 A
B = { 1, 2, 2, 4, 5 }		// 다중집합 B

AnB = { 1, 2, 2 }					// 교집합
AuB = { 1, 1, 2, 2, 3, 4, 5}		// 합집합

교집합의 경우 중복되는 원소의 가장 작은 반복횟수만큼, 합집합의 경우 중복되는 원소의 가장 큰 반복횟수만큼 인자가 들어간다.

✅ Solution

    for (var i = 0; i < arrStr1.length; i++) {
       if (arrStr2.indexOf(arrStr1[i]) >= 0) {
         intersection.push(arrStr2.splice(arrStr2.indexOf(arrStr1[i]), 1))
       }
       union.push(arrStr1[i])
     }

     for (var i = 0; i < arrStr2.length; i++) {
       union.push(arrStr2[i])
     }

첫번째 반복문에서 splice 함수를 사용하여 arrStr2 배열에서 arrStr1 배열과 중복되는 부분을 제거한 다음 두번째 반복문을 사용하여 중복되는 인자가 제거된 arrStr2의 배열을 union 배열에 추가하면 다중집합의 원소중복에 대해서 대응이 가능하다.