프로그래머스 - [1차] 뉴스 클러스터링 (Lv.2)

2 minute read

문제 링크

문제 접근1

  • 주어진 두 문자열의 알파벳을 소문자로 바꾸고, 다중집합을 만든다.
    • 알파벳이 아닌 문자는 무시하므로, 알파벳으로 문자가 2개 채워졌는지 확인을 해줘야 한다.
  • 교집합을 이루는 원소의 개수와 합집합을 이루는 원소의 개수를 구한다.
    • 길이가 짧은 집합과 길이가 긴 집합으로 나누어 탐색한다.
    • 길이가 짧은 집합의 원소가 길이가 긴 집합의 원소에 포함되는지 확인한다. 이때, 원소가 중복되는 경우가 있으므로 방문표시를 확인한다.
    • 합집합 원소의 개수는 위 과정에서 구한 교집합 원소의 개수에서 추가적인 탐색이 필요하다.
    • 교집합 탐색을 하면서 두 집합의 원소들 중 방문표시가 되지 않은 원소들을 합집합 원소의 개수에 더한다.

구현(All Pass)

#include <string>
#include <algorithm>
#include <iostream>
#include <cctype>

using namespace std;

vector<string> init_set(string str){
    vector<string> str_set;
    string tmp;
    for(int i=0; i<str.size(); i++){
        if(isalpha(str[i])){
            if(isupper(str[i]))
                tmp.push_back(tolower(str[i]));
            else
                tmp.push_back(str[i]);
        }
       if(isalpha(str[i+1])){
            if(isupper(str[i+1]))
                tmp.push_back(tolower(str[i+1]));
            else
                tmp.push_back(str[i+1]);
        }
        if(tmp.size()==2)
            str_set.push_back(tmp);
        tmp.clear();
    }
    return str_set;
}

float get_jacard(vector<string>& short_set, vector<string>& long_set){
    int inter_count = 0;
    int union_count = 0;
    float ret = 0.0f;
    int long_len = long_set.size();
    int short_len = short_set.size();
    vector<int> checked(long_len, 0);
    vector<int> checked_s(short_len, 0);
    
    string ref;
    for(int i=0; i<short_set.size(); i++){
        ref = short_set[i];
        for(int j=0; j<long_set.size(); j++){
            if(ref==long_set[j] && checked[j]==0){
                inter_count++;
                checked[j]=1;
                checked_s[i]=1;
                break;
            }
        }
    }
    
    union_count = inter_count;
    for(int i=0; i<short_set.size(); i++){
        if(checked_s[i]==0)
            union_count++;
    }
    for(int i=0; i<long_set.size(); i++){
        if(checked[i]==0)
            union_count++;
    }
    ret = (float)inter_count / union_count;
    
    return ret;
}


int solution(string str1, string str2) {
    int answer = 0;
    // init
    vector<string> str1_set = init_set(str1);
    vector<string> str2_set = init_set(str2);   
    vector<string> short_set = str1_set < str2_set ? str1_set : str2_set;
    vector<string> long_set = str1_set > str2_set ? str1_set : str2_set;
    
    // A=B
    if(short_set == long_set) return 65536;
    
    float jacard = get_jacard(short_set, long_set);
    answer = int(jacard * 65536);
    
    return answer;
}

문제 접근2

  • 알파벳 2개로 가능한 모든 조합은 26*26개이다.
  • 따라서, 각 case에 따라 배열의 인덱스를 지정해두고 str1 이 이루는 집합의 각 원소의 개수와 str2가 이루는 집합의 각 원소의 개수를 저장해둔다.
  • 교집합은 두 집합의 원소의 개수 중 적은 쪽을 선택하고, 합집합은 많은 쪽을 선택하면 된다.

구현(All Pass)

#include <string>
#include <vector>
#include <cctype>
#include <algorithm>
#include <iostream>

using namespace std;

int solution(string str1, string str2) {
    int answer = 0;
    // init
    // 가능한 모든 조합
    vector<int> set_1(26*26, 0);
    vector<int> set_2(26*26, 0);
    
    for(int i=0; i<str1.size(); i++){
        if(isalpha(str1[i]) && isalpha(str1[i+1])){
            if(isupper(str1[i])) str1[i]=tolower(str1[i]);
            if(isupper(str1[i+1])) str1[i+1]=tolower(str1[i+1]);
            set_1[(str1[i]-'a')*26 + (str1[i+1]-'a')]++;
        }
    }
    
    for(int i=0; i<str2.size(); i++){
        if(isalpha(str2[i]) && isalpha(str2[i+1])){
            if(isupper(str2[i])) str2[i]=tolower(str2[i]);
            if(isupper(str2[i+1])) str2[i+1]=tolower(str2[i+1]);
            set_2[(str2[i]-'a')*26 + (str2[i+1]-'a')]++;
        }
    }
    
    int inter_cnt = 0, union_cnt=0;
    for(int i=0; i<set_1.size(); i++){
        inter_cnt += min(set_1[i], set_2[i]);
        union_cnt += max(set_1[i], set_2[i]);
    }
    if(union_cnt==0) 
        answer = 65536;
    else
        answer = int(((float)inter_cnt/union_cnt) * 65536);
    
    return answer;
}

Leave a comment