검색어 word와 여러 웹페이지 HTML이 주어진다. 각 웹페이지의 기본 점수와 다른 페이지로부터 받은 링크 점수를 더한 매칭 점수를 계산한 뒤, 점수가 가장 높은 페이지의 인덱스를 반환한다.
word가 등장한 횟수페이지를 읽으면서 바로 링크 점수를 계산하면, 아직 다른 페이지의 URL이나 기본 점수를 알 수 없다. 따라서 아래처럼 두 단계로 나눈다.
URL을 키로, 페이지 인덱스를 값으로 저장하는 딕셔너리를 만들면 링크의 도착 페이지를 빠르게 찾을 수 있다.
기본 점수에서 검색어는 알파벳이 아닌 문자로 양쪽이 구분되어야 한다. 예를 들어 MuziMuzi는 muzi가 두 번 등장한 것으로 세면 안 된다.
본문에서 알파벳으로 이루어진 단어만 추출한 뒤 검색어와 비교하면 이 조건을 자연스럽게 처리할 수 있다.
words = re.findall(r"[a-z]+", text.lower())
basic_score = sum(token == word for token in words)
<body> 영역만 가져오고 HTML 태그를 공백으로 바꾼 뒤 단어를 추출한다. 이렇게 하면 URL이나 태그 속성에 포함된 문자열은 기본 점수에 포함되지 않는다.
url_to_index에 저장한다.기본 점수 / 외부 링크 수를 연결된 페이지에 더한다.>일 때만 답을 갱신한다.import re
def solution(word, pages):
word = word.lower()
page_count = len(pages)
# URL -> pages에서의 인덱스
url_to_index = {}
basic_scores = [0] * page_count
outgoing_links = [[] for _ in range(page_count)]
# 1단계: 각 페이지의 정보 수집
for index, page in enumerate(pages):
url = re.search(
r'<meta property="og:url" content="([^"]+)"',
page
).group(1)
url_to_index[url] = index
body = re.search(
r"<body>(.*?)</body>",
page,
re.DOTALL | re.IGNORECASE
).group(1)
# 태그를 제외한 본문 텍스트에서 알파벳 단어만 추출한다.
text = re.sub(r"<[^>]+>", " ", body).lower()
words = re.findall(r"[a-z]+", text)
basic_scores[index] = sum(token == word for token in words)
# 현재 페이지가 가리키는 모든 외부 링크
outgoing_links[index] = re.findall(r'<a href="([^"]+)"', page)
# 기본 점수로 매칭 점수를 초기화한다.
matching_scores = [float(score) for score in basic_scores]
# 2단계: 각 페이지의 링크 점수를 목적지 페이지에 분배한다.
for source_index, links in enumerate(outgoing_links):
if not links:
continue
shared_score = basic_scores[source_index] / len(links)
for url in links:
destination_index = url_to_index.get(url)
# 입력으로 주어진 페이지에 없는 URL은 점수를 받을 대상이 없다.
if destination_index is not None:
matching_scores[destination_index] += shared_score
# 동점이면 먼저 등장한 페이지를 유지한다.
answer = 0
for index in range(1, page_count):
if matching_scores[index] > matching_scores[answer]:
answer = index
return answer
url_to_index: 외부 링크 URL이 어느 페이지를 가리키는지 찾는다.basic_scores: 각 페이지가 직접 얻은 검색어 등장 횟수다.outgoing_links: 각 페이지가 다른 페이지로 보내는 링크 목록이다.shared_score: 현재 페이지가 링크 하나에 전달하는 점수다. 이 값은 매칭 점수가 아니라 기본 점수를 기준으로 계산한다.matching_scores: 기본 점수와 다른 페이지에서 받은 링크 점수의 합이다.예를 들어 어떤 페이지의 기본 점수가 6이고 외부 링크가 3개라면, 링크 하나당 6 / 3 = 2점씩 전달한다.
L: 모든 HTML 문자열 길이의 합E: 모든 외부 링크 개수의 합정규식으로 HTML을 분석하는 데 O(L), 링크 점수를 분배하는 데 O(E)가 걸린다.
O(L + E)O(P + E) (P는 페이지 수)이 문제의 핵심은 페이지 정보를 먼저 전부 수집한 뒤 링크 점수를 분배하는 것이다. 또한 동점 처리 조건 때문에 최댓값을 찾을 때는 >=가 아니라 >를 사용해야 한다.