문제주소 :programmers.co.kr/learn/courses/30/lessons/42893
<문제 설명>
문제 설명
매칭 점수
프렌즈 대학교 조교였던 제이지는 허드렛일만 시키는 네오 학과장님의 마수에서 벗어나, 카카오에 입사하게 되었다.
평소에 관심있어하던 검색에 마침 결원이 발생하여, 검색개발팀에 편입될 수 있었고, 대망의 첫 프로젝트를 맡게 되었다.
그 프로젝트는 검색어에 가장 잘 맞는 웹페이지를 보여주기 위해 아래와 같은 규칙으로 검색어에 대한 웹페이지의 매칭점수를 계산 하는 것이었다.
- 한 웹페이지에 대해서 기본점수, 외부 링크 수, 링크점수, 그리고 매칭점수를 구할 수 있다.
- 한 웹페이지의 기본점수는 해당 웹페이지의 텍스트 중, 검색어가 등장하는 횟수이다. (대소문자 무시)
- 한 웹페이지의 외부 링크 수는 해당 웹페이지에서 다른 외부 페이지로 연결된 링크의 개수이다.
- 한 웹페이지의 링크점수는 해당 웹페이지로 링크가 걸린 다른 웹페이지의 기본점수 ÷ 외부 링크 수의 총합이다.
- 한 웹페이지의 매칭점수는 기본점수와 링크점수의 합으로 계산한다.
예를 들어, 다음과 같이 A, B, C 세 개의 웹페이지가 있고, 검색어가 hi라고 하자.
이때 A 웹페이지의 매칭점수는 다음과 같이 계산할 수 있다.
- 기본 점수는 각 웹페이지에서 hi가 등장한 횟수이다.
- A,B,C 웹페이지의 기본점수는 각각 1점, 4점, 9점이다.
- 외부 링크수는 다른 웹페이지로 링크가 걸린 개수이다.
- A,B,C 웹페이지의 외부 링크 수는 각각 1점, 2점, 3점이다.
- A 웹페이지로 링크가 걸린 페이지는 B와 C가 있다.
- A 웹페이지의 링크점수는 B의 링크점수 2점(4 ÷ 2)과 C의 링크점수 3점(9 ÷ 3)을 더한 5점이 된다.
- 그러므로, A 웹페이지의 매칭점수는 기본점수 1점 + 링크점수 5점 = 6점이 된다.
검색어 word와 웹페이지의 HTML 목록인 pages가 주어졌을 때, 매칭점수가 가장 높은 웹페이지의 index를 구하라. 만약 그런 웹페이지가 여러 개라면 그중 번호가 가장 작은 것을 구하라.
제한사항
- pages는 HTML 형식의 웹페이지가 문자열 형태로 들어있는 배열이고, 길이는 1 이상 20 이하이다.
- 한 웹페이지 문자열의 길이는 1 이상 1,500 이하이다.
- 웹페이지의 index는 pages 배열의 index와 같으며 0부터 시작한다.
- 한 웹페이지의 url은 HTML의 <head> 태그 내에 <meta> 태그의 값으로 주어진다.
- 예를들어, 아래와 같은 meta tag가 있으면 이 웹페이지의 url은 https://careers.kakao.com/index 이다.
- <meta property="og:url" content="https://careers.kakao.com/index" />
- 한 웹페이지에서 모든 외부 링크는 <a href="https://careers.kakao.com/index"\>의 형태를 가진다.
- <a> 내에 다른 attribute가 주어지는 경우는 없으며 항상 href로 연결할 사이트의 url만 포함된다.
- 위의 경우에서 해당 웹페이지는 https://careers.kakao.com/index 로 외부링크를 가지고 있다고 볼 수 있다.
- 모든 url은 https:// 로만 시작한다.
- 검색어 word는 하나의 영어 단어로만 주어지며 알파벳 소문자와 대문자로만 이루어져 있다.
- word의 길이는 1 이상 12 이하이다.
- 검색어를 찾을 때, 대소문자 구분은 무시하고 찾는다.
- 예를들어 검색어가 blind일 때, HTML 내에 Blind라는 단어가 있거나, BLIND라는 단어가 있으면 두 경우 모두 해당된다.
- 검색어는 단어 단위로 비교하며, 단어와 완전히 일치하는 경우에만 기본 점수에 반영한다.
- 단어는 알파벳을 제외한 다른 모든 문자로 구분한다.
- 예를들어 검색어가 "aba" 일 때, "abab abababa"는 단어 단위로 일치하는게 없으니, 기본 점수는 0점이 된다.
- 만약 검색어가 "aba" 라면, "aba@aba aba"는 단어 단위로 세개가 일치하므로, 기본 점수는 3점이다.
- 결과를 돌려줄때, 동일한 매칭점수를 가진 웹페이지가 여러 개라면 그중 index 번호가 가장 작은 것를 리턴한다
- 즉, 웹페이지가 세개이고, 각각 매칭점수가 3,1,3 이라면 제일 적은 index 번호인 0을 리턴하면 된다.
입출력 예 #1
- word : blind
- pages :
- ["<html lang=\"ko\" xml:lang=\"ko\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n <meta charset=\"utf-8\">\n <meta property=\"og:url\" content=\"https://a.com\"/>\n</head> \n<body>\nBlind Lorem Blind ipsum dolor Blind test sit amet, consectetur adipiscing elit. \n<a href=\"https://b.com\"> Link to b </a>\n</body>\n</html>", "<html lang=\"ko\" xml:lang=\"ko\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n <meta charset=\"utf-8\">\n <meta property=\"og:url\" content=\"https://b.com\"/>\n</head> \n<body>\nSuspendisse potenti. Vivamus venenatis tellus non turpis bibendum, \n<a href=\"https://a.com\"> Link to a </a>\nblind sed congue urna varius. Suspendisse feugiat nisl ligula, quis malesuada felis hendrerit ut.\n<a href=\"https://c.com\"> Link to c </a>\n</body>\n</html>", "<html lang=\"ko\" xml:lang=\"ko\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n <meta charset=\"utf-8\">\n <meta property=\"og:url\" content=\"https://c.com\"/>\n</head> \n<body>\nUt condimentum urna at felis sodales rutrum. Sed dapibus cursus diam, non interdum nulla tempor nec. Phasellus rutrum enim at orci consectetu blind\n<a href=\"https://a.com\"> Link to a </a>\n</body>\n</html>"]
- pages는 다음과 같이 3개의 웹페이지에 해당하는 HTML 문자열이 순서대로 들어있다.
Blind Lorem Blind ipsum dolor Blind test sit amet, consectetur adipiscing elit. Link to b
Suspendisse potenti. Vivamus venenatis tellus non turpis bibendum, Link to a blind sed congue urna varius. Suspendisse feugiat nisl ligula, quis malesuada felis hendrerit ut. Link to c
Ut condimentum urna at felis sodales rutrum. Sed dapibus cursus diam, non interdum nulla tempor nec. Phasellus rutrum enim at orci consectetu blind Link to a
위의 예를 가지고 각각의 점수를 계산해보자.
- 기본점수 및 외부 링크수는 아래와 같다.
- a.com의 기본점수는 3, 외부 링크 수는 1개
- b.com의 기본점수는 1, 외부 링크 수는 2개
- c.com의 기본점수는 1, 외부 링크 수는 1개
- 링크점수는 아래와 같다.
- a.com의 링크점수는 b.com으로부터 0.5점, c.com으로부터 1점
- b.com의 링크점수는 a.com으로부터 3점
- c.com의 링크점수는 b.com으로부터 0.5점
- 각 웹 페이지의 매칭 점수는 다음과 같다.
- a.com : 4.5 점
- b.com : 4 점
- c.com : 1.5 점
따라서 매칭점수가 제일 높은 첫번째 웹 페이지의 index인 0을 리턴 하면 된다.
입출력 예 #2
- word : Muzi
- pages :
- ["<html lang=\"ko\" xml:lang=\"ko\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n <meta charset=\"utf-8\">\n <meta property=\"og:url\" content=\"https://careers.kakao.com/interview/list\"/>\n</head> \n<body>\n<a href=\"https://programmers.co.kr/learn/courses/4673\"></a>#!MuziMuzi!)jayg07con&&\n\n</body>\n</html>", "<html lang=\"ko\" xml:lang=\"ko\" xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n <meta charset=\"utf-8\">\n <meta property=\"og:url\" content=\"https://www.kakaocorp.com\"/>\n</head> \n<body>\ncon%\tmuzI92apeach&2<a href=\"https://hashcode.co.kr/tos\"></a>\n\n\t^\n</body>\n</html>"]
- pages는 다음과 같이 2개의 웹페이지에 해당하는 HTML 문자열이 순서대로 들어있다.
#!MuziMuzi!)jayg07con&&
con% muzI92apeach&2 ^
- 기본점수 및 외부 링크수는 아래와 같다.
- careers.kakao.com/interview/list 의 기본점수는 0, 외부 링크 수는 1개
- www.kakaocorp.com 의 기본점수는 1, 외부 링크 수는 1개
- 링크점수는 아래와 같다.
- careers.kakao.com/interview/list 의 링크점수는 0점
- www.kakaocorp.com 의 링크점수는 0점
- 각 웹 페이지의 매칭 점수는 다음과 같다.
- careers.kakao.com/interview/list : 0점
- www.kakaocorp.com : 1 점
따라서 매칭점수가 제일 높은 두번째 웹 페이지의 index인 1을 리턴 하면 된다.
<풀이법>
▒ 한줄 개념: 정규식 ▒
정규식을 이용하여 문자열을 파싱하고, 그 데이터를 사용하여 푸는 문제이다.
문제풀이 과정은 다음과 같다.
1. Link 객체 구현:
기본점수, 외부 링크들, 링크 점수, 인덱스
를 가지는 Link 클래스를 만들어주었다.
2. 자기 자신의 url 링크 추출:
Pattern home_url_pattern = Pattern.compile("<meta property=\"og:url\" content=\"(\\S*)\"");
위 정규식을 이용하여 자기 자신의 url을 추출해주었다. 이 url을 기반으로 새로운 Link 객체를 생성한다.
3. 연결된 외부 링크들 추출:
Pattern url_pattern = Pattern.compile("<a href=\"https://(\\S*)\"");
위 정규식을 이용하여 연결된 외부 링크들을 추출해준다. 추출한 외부 링크들을 Link 객체의 외부 링크들
변수에 차례차례 넣어준다.
4. word 등장 횟수(기본점수) 계산:
숫자, 특수문자 등 알파벳이 아닌 모든 문자가 각 단어 사이의 구분자가 된다.
body = pages[i].split("<body>")[1].split("</body>")[0].replaceAll("[0-9]", " ");
Pattern word_pattern = Pattern.compile("\\b(?i)"+word+"\\b");
따라서 편의를 위해 <body> 태그 내 모든 숫자를 공백(" ")
으로 replace해주었다. 따라서 단어를 검색하는 정규식이 양쪽에 특수문자가 존재하는지만 판별하면 되므로, 단순화되었다. (?i)
는 대소문자 구분을 하지 않도록 하기 위해 넣어주었다.
5. 링크점수 계산:
각 페이지에서 자신의 외부 링크에 대해 링크점수를 더해준다. 페이지 A
에서 페이지 B
로 자신의 기본점수 / 외부 링크 수
의 점수를 보내준다고 생각하면 된다.
6. 총점(매칭점수)가 가장 높은 페이지 확인:
단순 반복문을 사용해 max값을 가진 페이지를 찾으면 된다.
<주의사항>
url을 검사할때, https:// 검색에서 (.*)
을 사용할 때는 55점에서 머물렀는데,(\\S*)
를 사용하니 100점으로 통과되었다. 이유는 잘 모르겠다. 공부가 더 필요할 것 같다.
<코드(Java)>
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class Solution {
HashMap<String, Link> Links;
public int solution(String word, String[] pages) {
Links = new HashMap<>();
Pattern home_url_pattern = Pattern.compile("<meta property=\"og:url\" content=\"(\\S*)\"");
Pattern url_pattern = Pattern.compile("<a href=\"https://(\\S*)\"");
Pattern word_pattern = Pattern.compile("\\b(?i)"+word+"\\b");
Matcher url_matcher, word_matcher, home_url_matcher;
ArrayList<String> ext_urls;
Link new_Link;
String body;
String home_url = "";
for(int i = 0; i < pages.length; i++){
url_matcher = url_pattern.matcher(pages[i]);
home_url_matcher = home_url_pattern.matcher(pages[i]);
if(home_url_matcher.find()){
home_url = home_url_matcher.group().split("=")[2].replaceAll("\"", "");
}
new_Link = new Link(i, home_url);
ext_urls = new ArrayList<>();
while(url_matcher.find()) {
ext_urls.add(url_matcher.group().split("\"")[1]);
}
new_Link.setExtUrls(ext_urls);
body = pages[i].split("<body>")[1].split("</body>")[0].replaceAll("[0-9]", " ");
word_matcher = word_pattern.matcher(body);
int word_cnt = 0;
while(word_matcher.find())
word_cnt++;
new_Link.point = word_cnt;
new_Link.setLinkPoint();
Links.put(new_Link.url, new_Link);
}
for(Link link: Links.values()){
for(String ext_url : link.ext_urls){
if(Links.containsKey(ext_url)){
Links.get(ext_url).point += link.link_point;
}
}
}
double max_point = 0;
int answer = 0;
for(Link link: Links.values()){
if(link.point == max_point){
if(link.index < answer)
answer = link.index;
}
else if(link.point > max_point) {
answer = link.index;
max_point = link.point;
}
}
return answer;
}
class Link{
String url;
String[] ext_urls;
double point, link_point;
int index;
public Link(int i, String url){
this.index = i;
this.url = url;
}
public void setExtUrls(ArrayList<String> urls){
this.ext_urls = new String[urls.size()];
for(int i = 0; i < urls.size(); i++){
ext_urls[i] = urls.get(i);
}
}
public void setLinkPoint(){
this.link_point = this.point / ext_urls.length;
}
}
}
더 많은 코드 보기(GitHub) : github.com/dwkim-97/CodingTest
'Programmers' 카테고리의 다른 글
[프로그래머스] 스티커 모으기(2) / Java (0) | 2021.03.24 |
---|---|
[프로그래머스] 카드 짝 맞추기 / Java (2) | 2021.03.24 |
[프로그래머스] 거스름돈 / Java (0) | 2021.03.22 |
[프로그래머스] 외벽 점검 / Java (3) | 2021.03.22 |
[프로그래머스] 길 찾기 게임 / Java (0) | 2021.03.22 |