읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 외벽 점검

문제 풀이

그리디인줄 알았는데 브루트포스였다. 친구의 거리를 정렬해서 도입해도 특정 친구가 반드시 특정 지점에 가야함 정답이 성립하는 케이스가 존재하기 때문으로 보인다. 주어진 모든 친구의 투입 경우의 수를 순열로 생성해 대입해야 한다. 그리고 원형 구조는 길이를 정확히 두 배로 늘려서 해결할 수 있음을 이 문제를 통해 알 수 있었다.

길이를 두 배로 늘렸어도 탐색 시작 좌표는 기존 배열 내로 유지해야 하므로 늘리기 전에 전체 취약점 개수를 저장해야 한다. 매 출발 포인터에 대해 취약점 배열을 생성하고 모든 친구 투입 순서 경우에 대해 투입 시 카운트를 1 올리고 친구의 좌표를 0에서 1씩 더해 현재 친구의 탐색 지점과 탐색 가능 거리를 설정한다. 그리고 모든 취약점 포인트에 대해 현재 친구의 탐색 거리와 비교하여 탐색 거리가 만료되면 친구를 교체해되 전부 투입했으면 break한다.

  • 반시계 회전은 시계 방향에서 더 진행함을 의미하므로 외벽 길이만큼 더해서 뒤에 붙인다.
  • 모든 친구 투입 경우의 수를 순열로 생성한다.
  • 모든 출발 지점에 대해 순회를 하는 반복문을 정의한다.
  • 시작 지점에 따른 취약점 거리 배열을 정의한다.
  • 친구 순서에 대해 순회하는 반복문 정의한다.
  • 현재 친구가 순회 가능한 거리보다 크다면 친구를 교체한다.
  • 순회가 끝나면 투입된 친구 개수를 업데이트한다.

python 코드

from itertools import permutations
def solution(n, weak, dist):
    weak_len = len(weak)
    for i in range(weak_len):
        weak.append(weak[i] + n)
    dist_permu_set = list(permutations(dist, len(dist)))
    answer = len(dist) + 1
    for i in range(weak_len):
        start = [weak[i + j] for j in range(weak_len)]
        for dist_p in dist_permu_set:
            result = 1
            dist_c = 0
            dist_v = start[0] + dist_p[dist_c]
            for k in range(weak_len):
                if dist_v < start[k]:
                    result += 1
                    dist_c += 1
                    if result > len(dist_p):
                        break
                    dist_v = start[k] + dist_p[dist_c]
            answer = min(answer, result)
    if answer > len(dist):
        return -1
    else:
        return answer

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 블록 이동하기  (0) 2021.09.10
Programmers. 가사 검색  (0) 2021.09.10
Programmers. 매칭 점수  (0) 2021.09.10
Programmers. 프렌즈4블록  (0) 2021.09.10
Programmers. 경주로 건설  (0) 2021.09.10

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 매칭 점수

문제 풀이

처음 문제를 봤을 땐 대체 뭐 이리 더럽나 싶었는데 정규식으로 얼마나 깔끔하게 파싱할 수 있는지 묻는 문제였다.

먼저 단어는 대소문자 구분이 없으므로 전부 소문자로 만들어 준다. 그리고 각 페이지의 점수 계산을 위해 단어가 포함된 개수와 외부 링크 수를 저장할 hash 구조, 자신에게 링크를 보내오는 url을 저장할 hash 구조, 좌표를 기준으로 어떤 url을 갖는 페이지인지 저장할 hash구조 총 3개의 hash 구조를 정의해야 한다.

먼저 각 페이지에 대해 역시나 전부 소문자로 만들어두고 정규표현식을 사용해 url을 ㅜ출한다.

  • 정규표현식 : r'<meta[^>]*content="http://(\S+)"/>'

괄호는 search 시 매칭되는 요소를 저장하며 \S는 공백 등을 제외한 문자를 의미한다.

페이지에 보함된 단어를 세야하는데 [a-z]+이면 알파벳이 1개 이상 반복되는 모든 문자를 의미하므로 공백, 특수문자를 모두 거를 수 있다. 조회된 모든 단어들에 대해 word와 비교하여 일치하면 카운트를 1 올린다.

단어 카운트를 구했으므로 해당 페이지의 점수를 기록할 hash 구조에 현재 url을 키로 찾아 삽입한다. 외부 url은 중복을 염두해 우선 set을 선언한 뒤 정규식에 매칭되는 전체 결과들에 대해 카운트하고 그 길이를 역시 점수 hash 구조에 현재 url을 키로 삽입한다. 이로써 링크 점수가 결정되었다.

외부 링크 입장에선 현재 url이 연결을 보내왔으므로 링크를 보내는 url을 저장할 hash 구조에 각 원소를 key값으로 삼아 현재 url을 삽입한다.

모든 페이지에 대해 순회가 끝나면 페이지 좌표를 기준으로 url을 찾고 해당 url의 기본 점수를 계산하고 외부로부터 들어온 모든 url에 대해 링크 점수를 계산하여 더한 뒤 최댓값인지 갱신한다.

  • 좌표에서 url로 변환할 hash, 현재 url의 단어 매칭과 외부 링크를 저장할 hash, 자신에게 링크를 건 링크들을 저장할 hash 선언
  • word와 각 페이지들을 소문자로 변환
  • 정규식 r'<meta[^>]*content="https://(\S*)"/>'을 사용, url 추출
  • 단어 r'[a-z]+를 사용, 모든 단어 추출하여 비교, 카운트
  • 외부 링크 r'<a href="https://(\S*)"> 사용하여 모든 외부연결 url 추출, 개수를 저장하고 각 링크에 대해 외부접속링크 목록에 현재 url 추가
  • 페이지를 모두 순회하여 점수 계산, 최댓값 갱신

python 코드

import re
from collections import defaultdict
def solution(word, pages):
    answer = 0
    word = word.lower()
    url_to_idx = defaultdict(list)
    url_score = defaultdict(list)
    url_from_link = defaultdict(list)
    for i in range(len(pages)):
        page = pages[i].lower()
        url = re.search(r'<meta[^>]*content="https://(\S*)"/>', page).group(1)
        word_cnt = 0
        for find in re.findall(r'[a-z]+', page):
            if find == word:
                word_cnt += 1
        url_score[url].append(word_cnt)
        url_to_idx[i] = url
        wordCnt = 0
        exLinkSet = set()
        for e in re.findall(r'<a href="https://(\S*)">', page):
            exLinkSet.add(e)
        url_score[url].append(len(exLinkSet))
        for e in exLinkSet:
            url_from_link[e].append(url)
    ans_score = 0
    anser = 0
    for i in range(len(pages)):
        url = url_to_idx[i]
        basic_score = url_score[url][0]
        link_score = 0
        for e in url_from_link[url]:
            link_score += url_score[e][0] / url_score[e][1]
        score = basic_score + link_score
        if score > ans_score:
            ans_score = score
            answer = i
    return answer

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 가사 검색  (0) 2021.09.10
Programmers. 외벽 점검  (0) 2021.09.10
Programmers. 프렌즈4블록  (0) 2021.09.10
Programmers. 경주로 건설  (0) 2021.09.10
Programmers. 표 편집  (0) 2021.09.10

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 프렌즈4블록

문제 풀이

블록을 깨고 나머지를 아래로 내려보낸다면 행처리에 상당히 애를 먹겠으나 행과 열을 바꾸면 처리가 쉽다는 것을 망각해버렸다. 행과 열을 바꾼 2차월 배열을 생성하고 전체 면적에 4칸을 순회탐색하여 깰 블록이 있는지 set에 넣고 있다면 터뜨리고 나머지 블록을 몰아넣는 과정을 반복하여 더 이상 전체 순회해도 터뜨릴 블록이 없을 때까지 수행한다.

  • 행과 열을 서로 바꾼 2차원 배열을 선언한다.
  • 전체 보드에서 4칸으로 탐색가능한 전체 범위를 탐색, 4칸이 빈 블록이 아니고 서로 값이 같다면 터뜨릴 블록의 좌표를 set에 입력
  • 순회가 끝나고 터뜨릴 좌표가 담긴 set의 원소를 전부 터진 상태로 바꾸며 개수를 센다.
  • 각 행에 대해 터진 원소의 수를 count하여 그만큼 빈 블록을 생성하고 그 뒤에 터지지 않은 블록을 압축한 배열을 붙이고 다시 탐색한다.
  • 더 이상 터지는 블록이 없을 때까지 탐색

python 코드

def solution(m, n, board):
    def check(i, j):
        a = grid[i][j]
        b = grid[i + 1][j]
        c = grid[i][j + 1]
        d = grid[i + 1][j + 1]
        if a == b == c == d != "_":
            pop_set.add((i, j))
            pop_set.add((i + 1, j))
            pop_set.add((i, j + 1))
            pop_set.add((i + 1, j + 1))
            return True
    grid = []
    for i in range(len(board[0])):
        buf = []
        for j in range(len(board)):
            buf.append(board[j][i])
        grid.append(buf)
    answer = 0
    while True:
        pop_set = set()
        for r in range(len(grid) - 1):
            for c in range(len(grid[0]) - 1):
                check(r, c)
        if len(pop_set) == 0:
            break
        answer += len(pop_set)
        for i, j in list(pop_set):
            grid[i][j] = "X"
        for i, g in enumerate(grid):
            grid[i] = ["_"] * g.count("X") + [b for b in g if b != "X"]
    return answer

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 외벽 점검  (0) 2021.09.10
Programmers. 매칭 점수  (0) 2021.09.10
Programmers. 경주로 건설  (0) 2021.09.10
Programmers. 표 편집  (0) 2021.09.10
Programmers. 자물쇠와 열쇠  (0) 2021.09.10

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 경주로 건설

문제 풀이

특정 지점에 도달할 때 비용이 최소로 들어야 한다는 점에서 다익스트라 알고리즘을 사용해야 한다는 점은 알아채고 적용했으나 테스트케이스 단 하나를 통과하지 못했다. 라이트업을 참고하니 특정 칸에서 어떤 경로가 더 비용이 적더라도 그 경로가 마지막 칸까지의 비용이 최소인 경로임은 보장하지 않는다는 점이다. 즉, 더 비싸더라도 다른 방향에서 접근한 경로가 마지막에서 더 저렴할 수 있다는 의미이다. 따라서, 다익스트라를 사용하되 접근할 수 있는 4개의 방향에 대한 비용을 체크하고 마지막 칸에 대해 비용 배열의 최솟값을 리턴해야 한다.

  • 모든 칸에 대해 4가지 방향으로 접근한 경로의 비용을 저장할 3차원 방문 배열 정의
  • 초항은 0,0에서 출발해 오른쪽과 아래쪽으로 진행이 가능하기에 board상태 체크하고 비용 업데이트 후 큐에 넣는다,
  • 다익스트라를 진행하는 동안 가고자하는 노드의 현재 방향에 대한 기록된 비용보다 크다면 skip하고 그렇지 않으면 갱신한 뒤 4가지 방향에 대해 범위 체크, 방향이 다르면 커브를 의미하므로 500을 더해준다.
  • 큐가 모두 비워질 때까지 탐색한 후 마지막 칸이 갖는 비용의 최솟값 리턴

python 코드

from collections import deque
def solution(board):
    N = len(board)
    INF = float('inf')
    visited = [[[INF] * 4 for _ in range(N)] for _ in range(N)]
    dr = [0, 0, 1, -1]
    dc = [1, -1, 0, 0]
    q = []
    if board[1][0] == 0:
        visited[1][0][2] = 100
        q.append((100, 1, 0, 2))
    if board[0][1] == 0:
        visited[0][1][0] = 100
        q.append((100, 0, 1, 0))
    visited[0][0] = [0,0,0,0]
    q = deque(q)
    while q:
        cost, r, c, d = q.popleft()
        if visited[r][c][d] >= cost:
            visited[r][c][d] = cost
        else:
            continue
        for i in range(4):
            nd = int(i)
            nr, nc = r + dr[nd], c + dc[nd]
            if 0 > nr or nr >= N or 0 > nc or nc >= N:
                continue
            ncost = cost + 100
            if d != nd:
                ncost += + 500
            if visited[nr][nc][nd] < ncost or board[nr][nc] == 1:
                continue
            visited[nr][nc][nd] = ncost
            q.append((ncost, nr, nc, nd))
    return min(visited[-1][-1])

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 매칭 점수  (0) 2021.09.10
Programmers. 프렌즈4블록  (0) 2021.09.10
Programmers. 표 편집  (0) 2021.09.10
Programmers. 자물쇠와 열쇠  (0) 2021.09.10
Programmers. 징검다리 건너기  (0) 2021.09.09

읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 배운 점을 정리한 글입니다.

문제 링크

Programmers. 표 편집

문제 풀이

유니온 파인드로 풀 수 있지 않을까 생각했지만 삭제 순간은 현재 지점을 지우면 되지만 복구 지점은 이전 위치에 복구해야 한다는 점과 마지막으로 삭제한 노드의 양쪽 지점은 삭제되지 않은 상태라는 점을 참고해 이중 연결리스트라면 O(1)에 가능할 수도 있겠다 생각했다. C라면 포인터의 개념을, 파이썬이라면 메모리가 가리키는 변수공간에 대한 고려를 할 수 있는가 판단하는 문제였다고 생각한다. 시간 내에 문제를 해결하긴 했지만 해설을 보니 세그먼트나 팬윅트리로도 해결할 수 있다는데 일단 스킵.

  • 현재 값, 좌측, 우측 포인터를 변수로 갖는 리스트 노드 정의
  • 커서를 다루기 위해 양쪽 끝을 -1로 무효 노드로 두고 사이에 배열을 순회하면서 넣는다. 별도의 동일한 변수를 연결시켜서 한 변수는 그대로 첫번째를 가리키게 한 뒤 k번 이동해서 초기 커서의 위치를 설정한다.
  • 각 명령에 따라 행동하되 C입력 시 스택에 삭제 노드의 왼, 오른쪽을 초기화하지 않고 그대로 스택에 넣어 복구 시 찾아갈 수 있도록 한다. 그리고 커서를 조건에 맞춰서 이동시킨다.
  • Z입력 시 스택에 가장 마지막으로 삭제된 노드를 가져와 이전 노드와 이후 노드를 변수에 할당해 다시 복구시킨다.

코드가 굉장히 길어지는 문제였는데도 정답률이 꽤 높다는게 좌절스럽다.

python 코드

class ListNode(object):
    def __init__(self, val, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def solution(n, k, cmd):
    cur = head = node = ListNode(-1)
    for i in range(n):
        new = ListNode(i)
        node.right = new
        new.left = node
        node = node.right
    new = tail = ListNode(-1)
    node.right = new
    new.left = node
    node = node.right
    for i in range(k + 1):
        cur = cur.right
    stack = []
    for i in range(len(cmd)):
        if len(cmd[i]) == 1:
            if cmd[i] == "C":
                if cur.right.val == -1:
                    stack.append(cur)
                    cur.left.right, cur.right.left, cur = cur.right, cur.left, cur.left
                else:
                    stack.append(cur)
                    cur.left.right, cur.right.left, cur = cur.right, cur.left, cur.right
            elif cmd[i] == "Z":
                reg_node = stack.pop()
                prev = reg_node.left
                nxt = reg_node.right
                prev.right = reg_node
                nxt.left = reg_node
        else:
            op, x = cmd[i].split()
            if op == "U":
                for _ in range(int(x)):
                    cur = cur.left
            elif op == "D":
                for _ in range(int(x)):
                    cur = cur.right
    answer = ["X" for _ in range(n)]
    buf = []
    while head:
        buf.append(head.val)
        head = head.right
    cnt = 0
    for b in buf[1:-1]:
        answer[b] = "O"
    return "".join(answer)

'Algorithms > Programmers' 카테고리의 다른 글

Programmers. 프렌즈4블록  (0) 2021.09.10
Programmers. 경주로 건설  (0) 2021.09.10
Programmers. 자물쇠와 열쇠  (0) 2021.09.10
Programmers. 징검다리 건너기  (0) 2021.09.09
Programmers. 호텔 방 배정  (0) 2021.09.09

+ Recent posts