📌뉴스 클러스터링
/* 문제 설명은 링크로 대체 */
📌풀이
먼저 문제에 몇가지 조건이 있다.
- 입력으로 들어온 문자열은 두 글자씩 끊어서 다중집합의 원소로 만든다.
- 자카드 유사도 방법을 이용한다.
- "AB"와 "Ab", "ab"는 같은 원소로 취급 (대소문자 구분 X)
- 원소의 중복을 허용하는 다중집합에 대해서 확장할 수 있다.
ex. 다중집합 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) = a, b의 교집합개수 / a,b의 합집합개수 )이라고 문제에 방법이 명시되어 있다.
집합 a와 b를 생성하였고, str1, str2를 모두 소문자로 변환한 뒤 split()함수를 구현하여 두글자씩 잘라 !문자!만 넣어주었다.
교집합의 개수와 합집합의 개수를 구해야 자카드 유사도를 구할 수 있다!
교집합의 개수는 2가지로 나눠서 구하였다.
- a집합을 돌면서 b집합에서 a원소와 동일한 값이 존재할 때 교집합으로 하고, a집합에서 동일 값을 지웠다.
- b집합에 대해서도 1과 동일한 방법으로 진행한다.
-> 이렇게 하면 두 집합을 각각 돌면서 교집합을 구할 수 있따.
합집합의 개수는 교집합의 개수와 원래 a집합과 b집합의 크기로 구할 수 있다. (수학공식 이용)
주의 할 점
- 교집합을 구하면서 동일 값을 지우기 때문에 a집합과 b집합의 크기 합은 미리 교집합 탐색 전에 진행해야 한다.
- 문제에서 각 집합이 공집합인 경우 return 해야 한다고 명시되어 있으므로 &&연산을 이용한다. //안하면 testcase 5에서 오류남
if(a.empty() && b.empty()) return 65536;
📌코드
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void split(string str, vector<string>& v){
for(int i = 0; i < str.size() - 1; i++){
string temp = str.substr(i, 2); //2개씩 잘라
//두 글자가 모두 문자인 경우에만 v에 추가
if(temp[0] >= 'a' && temp[0] <= 'z' && temp[1] >= 'a' && temp[1] <= 'z')
v.push_back(temp);
}
}
int solution(string str1, string str2) {
int gyo = 0, hab = 0;
double jacade = 0; //J(A, B)는 소수 값
//집합 a, b //2글자씩 잘라서 보관
vector<string> a;
vector<string> b;
//str1과 str2를 모두 소문자로 변환
transform(str1.begin(), str1.end(), str1.begin(), ::tolower);
transform(str2.begin(), str2.end(), str2.begin(), ::tolower);
split(str1, a); // str1 2개씩 잘라 집합 a 생성
split(str2, b); // str2 2개씩 잘라 집합 b 생성
//2개 집합 모두 빈 값 비교할 게 없으므로 -> 65536 return //testcase5
if(a.empty() && b.empty()) return 65536;
//두 집합의 개수를 더한다. (교집합을 찾은 경우, 집합에서 지울 것이기 때문에)
hab = a.size() + b.size();
//배열을 탐색하며 교집합의 개수을 구한다.
if(a.size() > b.size()){
for(auto iter : b){
auto find_str = find(a.begin(), a.end(), iter); //a집합에서 iter와 같은 문자열을 반환
if(find_str != a.end()){ //b집합에서 찾았다면
gyo++;
a.erase(find_str);
}
}
}else{
for(auto iter : a){
auto find_str = find(b.begin(), b.end(), iter); //b집합에서 iter와 같은 문자열을 반환
if(find_str != b.end()){ //a집합에서 찾았다면
gyo++;
b.erase(find_str);
}
}
}
//합집합의 개수는 a.size() + b.size() - 교집합.size() 이므로
hab -= gyo;
if(hab == 0) return 65536; //합집합이 0이라면 나눌 수 없으므로
else jacade = (double) gyo / (double) hab;
return jacade * 65536;
}
회고
처음에 코테 문제 풀기 시작할 땐 그냥 보이는대로 닥치는대로 코딩하기 시작했는데 요즘은 어려운 문제들과 로직이 필요한 문제들을 만나면서 새로운 습관이 생겼다.
- 연습장에 문제 예제를 정리해보면서 로직 찾기
- 주석을 달아가면서 코딩하기
확실히 이 2가지를 지키면서 코딩하니 전보다는 그래도 조금 더 나은 것 같다.
이 문제는 제법 재밌게 풀었던 ....
'코딩테스트 공부 > Programmers' 카테고리의 다른 글
[C++] level2 수식최대화 67257 (0) | 2021.08.21 |
---|---|
[C++] level2 [3차] n진수 게임 17687 (0) | 2021.08.21 |
[C++] level1 비밀지도 50067 (0) | 2021.08.21 |
[C++] level2 문자열 압축 50067 (0) | 2021.08.21 |
[C++] level2 2개 이하로 다른 비트 77885 (0) | 2021.08.21 |
댓글