<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>LeetCode Study</title><description>A LeetCode study log, and leetcode-study — the Claude Code plugin that works through a problem with you instead of solving it for you.</description><link>https://zeikar.dev/leetcode/</link><language>ko-KR</language><item><title>1477. Find Two Non-overlapping Sub-arrays Each With Target Sum</title><link>https://zeikar.dev/leetcode/posts/320/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/320/</guid><description>합이 target인 겹치지 않는 부분 배열 두 개를 골라서 길이의 합을 최소로 만드는 문제. 일단 arr[i] &gt;= 1이라 전부 양수다. 그래서 합이 target인 구간은 슬라이딩 윈도우로 죽 밀면서 찾아주면 된다. 겹치지 않게 두 개를 고르는 게 문제인데, 오른쪽 구간을 고정하면 왼쪽은 제일 짧은 것 하나만 알면 된다. minLen[i]를 i 이하에서 끝…</description><pubDate>Thu, 17 Sep 2026 23:43:10 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem Link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/find-two-non-overlapping-sub-arrays-each-with-target-sum/&quot;&gt;https://leetcode.com/problems/find-two-non-overlapping-sub-arrays-each-with-target-sum/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;합이 &lt;code&gt;target&lt;/code&gt;인 겹치지 않는 부분 배열 두 개를 골라서 길이의 합을 최소로 만드는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;일단 &lt;code&gt;arr[i] &gt;= 1&lt;/code&gt;이라 전부 양수다. 그래서 합이 &lt;code&gt;target&lt;/code&gt;인 구간은 슬라이딩 윈도우로 죽 밀면서 찾아주면 된다.&lt;/p&gt;
&lt;p&gt;겹치지 않게 두 개를 고르는 게 문제인데, &lt;strong&gt;오른쪽 구간을 고정&lt;/strong&gt;하면 왼쪽은 제일 짧은 것 하나만 알면 된다. &lt;code&gt;minLen[i]&lt;/code&gt;를 i 이하에서 끝나는 유효 구간의 최소 길이로 두면, i가 커질 때 후보가 추가만 되니까 이 값은 커질 수가 없다. 커질 수도 있나 했는데 아니었다. 그래서 이분탐색이나 구간 최소 자료구조 없이 그냥 밀면서 갱신해주면 된다.&lt;/p&gt;
&lt;p&gt;스캔은 두 번 했는데 에디토리얼은 한 번으로 끝낸다... &lt;code&gt;l &amp;#x3C;= r&lt;/code&gt;이라 윈도우를 찾은 시점에 &lt;code&gt;best[l]&lt;/code&gt;이 이미 계산되어 있어서, 갱신이랑 조합을 같은 루프에서 해주면 된다.&lt;/p&gt;
&lt;p&gt;시간복잡도는 &lt;code&gt;O(n)&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def minSumOfLengths(self, arr: list[int], target: int) -&gt; int:
        n = len(arr)
        arr.append(987654321)
        minLen = [987654321 for _ in range(n)]

        l, r = 0, 0
        s = 0
        while l &amp;#x3C;= r:
            if l == n:
                break
            
            if s &amp;#x3C; target:
                if r &gt; 1:
                    minLen[r-1] = min(minLen[r-1], minLen[r-2])
                s += arr[r]
                r += 1
            elif s == target:
                if r &gt; 1:
                    minLen[r-1] = min(minLen[r-1], minLen[r-2])
                minLen[r-1] = min(minLen[r-1], r - l)
                s += arr[r]
                r += 1
            else:
                s -= arr[l]
                l += 1
        
        ans = 987654321
        l, r = 0, 0
        s = 0
        while l &amp;#x3C;= r:
            if l == n:
                break
            
            if s &amp;#x3C; target:
                s += arr[r]
                r += 1
            elif s == target:
                if l &gt; 0:
                    ans = min(ans, minLen[l-1] + r - l)
                s += arr[r]
                r += 1
            else:
                s -= arr[l]
                l += 1

        return ans if ans != 987654321 else -1
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>1621. Number of Sets of K Non-Overlapping Line Segments</title><link>https://zeikar.dev/leetcode/posts/319/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/319/</guid><description>1차원 위에 놓인 점 n개에서 서로 겹치지 않는 선분 k개를 그리는 경우의 수를 구하는 문제. 선분은 점을 둘 이상 덮어야 하고 끝점은 공유할 수 있다. modular 나온 걸 보고 딱 DP겠거니 했다. 어제 2472처럼 겹치지 않게 k개를 고르는 문제인데 이번엔 최대 개수가 아니라 경우의 수다. 한 점에서 할 수 있는 건 이어붙이기, 끊고 넘어가기, 새로…</description><pubDate>Wed, 16 Sep 2026 11:24:32 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem Link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/number-of-sets-of-k-non-overlapping-line-segments/&quot;&gt;https://leetcode.com/problems/number-of-sets-of-k-non-overlapping-line-segments/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;1차원 위에 놓인 점 n개에서 서로 겹치지 않는 선분 k개를 그리는 경우의 수를 구하는 문제. 선분은 점을 둘 이상 덮어야 하고 끝점은 공유할 수 있다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;modular 나온 걸 보고 딱 DP겠거니 했다.&lt;/p&gt;
&lt;p&gt;어제 2472처럼 겹치지 않게 k개를 고르는 문제인데 이번엔 최대 개수가 아니라 경우의 수다. 한 점에서 할 수 있는 건 이어붙이기, 끊고 넘어가기, 새로 시작하기 세 가지인데 &lt;strong&gt;이어붙이는 중인지를 상태로 넣어야&lt;/strong&gt; 해서 3차원이 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;solve(i, j, c) = i번째 점을 볼 차례이고 선분을 j개 썼을 때의 경우의 수 (c는 이어붙이는 중인지)

c == 0: solve(i+1, j, 0) + solve(i+1, j+1, 1)
c == 1: solve(i+1, j, 1) + solve(i+1, j, 0) + solve(i+1, j+1, 1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;끝점을 공유할 수 있으니 &lt;code&gt;c == 1&lt;/code&gt;에서 끊자마자 새로 시작하는 &lt;code&gt;solve(i+1, j+1, 1)&lt;/code&gt;이 따로 필요하다. &lt;code&gt;j == k&lt;/code&gt;이고 &lt;code&gt;c == 0&lt;/code&gt;이면 남은 점은 그냥 두면 되니까 1을 돌려주면 된다.&lt;/p&gt;
&lt;p&gt;처음엔 또 편하게 &lt;code&gt;@cache&lt;/code&gt;로 짰다가 메모리 초과... 항목이 100만 개인데 인자 3개짜리 캐시는 항목당 130바이트 정도라 캐시만 125MB다. 3차원 배열로 바꾸니 통과.&lt;/p&gt;
&lt;p&gt;시간복잡도는 O(nk)&lt;/p&gt;
&lt;p&gt;참고로 수학적으로는 &lt;code&gt;C(n + k - 1, 2k)&lt;/code&gt;로 바로 나온다. stars and bars로 증명된다. 자세한건 &lt;a href=&quot;https://leetcode.com/problems/number-of-sets-of-k-non-overlapping-line-segments/editorial/&quot;&gt;에디토리얼&lt;/a&gt; 참고.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def numberOfSets(self, n: int, k: int) -&gt; int:

        dp = [[[ -1 for _ in range(2) ] for _ in range(k+1) ] for _ in range(n)]

        def solve(i, j, c):
            if j == k and c == 0:
                return 1
            if j &gt; k:
                return 0
            if i == n:
                return 0

            if dp[i][j][c] != -1:
                return dp[i][j][c]
            
            if c == 1:
                dp[i][j][c] = (solve(i+1, j, 1) + solve(i+1, j+1, 1) + solve(i+1, j, 0)) % (10**9+7)
                return dp[i][j][c]
            else:
                dp[i][j][c] = (solve(i+1, j, 0) + solve(i+1, j+1, 1)) % (10**9+7)
                return dp[i][j][c]
        
        return solve(0, 0, 0)
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>2472. Maximum Number of Non-overlapping Palindrome Substrings</title><link>https://zeikar.dev/leetcode/posts/318/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/318/</guid><description>문자열 s에서 길이가 k 이상인 팰린드롬 부분 문자열을 서로 겹치지 않게 최대 몇 개 고를 수 있는지 구하는 문제. 딱 보니 DP라서 처음에는 top-down으로 풀었다. 팰린드롬 판별도 isPalindrome(l, r)로 DP를 두면 전체가 O(n^2)이 된다. 처음엔 너무 편하게 둘 다 @cache를 붙였다가 메모리 초과가 났다... isPalindro…</description><pubDate>Tue, 15 Sep 2026 12:48:36 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem Link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-number-of-non-overlapping-palindrome-substrings/&quot;&gt;https://leetcode.com/problems/maximum-number-of-non-overlapping-palindrome-substrings/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;문자열 s에서 길이가 k 이상인 팰린드롬 부분 문자열을 서로 겹치지 않게 최대 몇 개 고를 수 있는지 구하는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;딱 보니 DP라서 처음에는 top-down으로 풀었다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;solve(i) = i 인덱스부터 고를 수 있는 팰린드롬의 최대 개수
solve(i) = max(solve(j + 1), s[i..j]가 길이 k 이상 팰린드롬이면 1 + solve(j + 1))   (i &amp;#x3C;= j &amp;#x3C; n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;팰린드롬 판별도 &lt;code&gt;isPalindrome(l, r)&lt;/code&gt;로 DP를 두면 전체가 O(n^2)이 된다.&lt;/p&gt;
&lt;p&gt;처음엔 너무 편하게 둘 다 &lt;code&gt;@cache&lt;/code&gt;를 붙였다가 메모리 초과가 났다... &lt;code&gt;isPalindrome&lt;/code&gt; 캐시만 200만 개라 350MB 가까이 먹는다. &lt;strong&gt;일반 2차원 배열로 바꾸니&lt;/strong&gt; 통과.&lt;/p&gt;
&lt;p&gt;bottom-up은 팰린드롬 표를 중심에서 양쪽으로 넓혀가면서 채우고, &lt;code&gt;dp[i+1]&lt;/code&gt;을 앞에서부터 채워주면 된다. dp 인덱스 때문에 좀 헤맸는데 거의 2배 빨라졌다.&lt;/p&gt;
&lt;p&gt;시간복잡도는 O(n^2)&lt;/p&gt;
&lt;p&gt;에디토리얼을 보니 &lt;strong&gt;그리디&lt;/strong&gt;로도 풀린다. 자세한건 &lt;a href=&quot;https://leetcode.com/problems/maximum-number-of-non-overlapping-palindrome-substrings/editorial/&quot;&gt;에디토리얼&lt;/a&gt; 참고.&lt;/p&gt;
&lt;p&gt;앞에서부터 보면서 길이 k 이상 팰린드롬 중 가장 빨리 끝나는 걸 고르고, 그 다음부터 다시 찾으면 된다. 왜 되는지 간단하게 정리하면&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;최대한 많이 넣어야 되니까 &lt;strong&gt;빨리 끝나는 게 무조건 이득&lt;/strong&gt;이다. 최적해의 첫 번째 팰린드롬을 가장 빨리 끝나는 걸로 바꿔도 끝나는 위치가 같거나 더 앞이라 뒤에 고른 것들과 겹치지 않는다. 즉, 개수는 그대로고 뒤에 남는 부분은 같거나 더 길어진다.&lt;/li&gt;
&lt;li&gt;길이는 &lt;strong&gt;k와 k+1만&lt;/strong&gt; 보면 된다. 팰린드롬은 양 끝을 하나씩 떼도 팰린드롬이라서 길이 k+2 이상이면 안쪽에 길이 k나 k+1짜리가 들어 있고, 그게 더 빨리 끝나니까 그걸 넣는 게 이득이다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;그래서 각 위치에서 길이 k, k+1짜리만 확인하면 O(nk)로 풀린다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;h2 id=&quot;user-content-top-down-dp-6845ms&quot;&gt;Top-Down DP (6845ms)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def maxPalindromes(self, s: str, k: int) -&gt; int:
        n = len(s)

        paldp = [[-1 for _ in range(n)] for _ in range(n)]
        def isPalindrome(l, r):            
            if l &gt;= r:
                return True
            if paldp[l][r] != -1:
                return paldp[l][r]

            paldp[l][r] = s[l] == s[r] and isPalindrome(l + 1, r - 1)
            return paldp[l][r]

        @cache
        def solve(i):
            if i == n:
                return 0

            ret = 0
            for j in range(i, n):
                if j - i + 1 &gt;= k and isPalindrome(i, j):
                    ret = max(ret, 1 + solve(j + 1))
                else:
                    ret = max(ret, solve(j + 1))
            return ret

        return solve(0)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;user-content-bottom-up-dp-3766ms&quot;&gt;Bottom-Up DP (3766ms)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def maxPalindromes(self, s: str, k: int) -&gt; int:
        n = len(s)

        paldp = [[False for _ in range(n)] for _ in range(n)]
        for i in range(n):
            l, r = i, i
            while l &gt;= 0 and r &amp;#x3C; n and s[l] == s[r]:
                paldp[l][r] = True
                l -= 1
                r += 1
            
            l, r = i, i + 1
            while l &gt;= 0 and r &amp;#x3C; n and s[l] == s[r]:
                paldp[l][r] = True
                l -= 1
                r += 1

        dp = [0 for _ in range(n+1)]
        for i in range(n):
            dp[i+1] = dp[i]
            for j in range(0, i+1):
                if i - j + 1 &gt;= k and paldp[j][i]:
                    dp[i+1] = max(dp[i+1], dp[j] + 1)
                else:
                    dp[i+1] = max(dp[i+1], dp[j])

        return dp[n]
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>hard</category></item><item><title>836. Rectangle Overlap</title><link>https://zeikar.dev/leetcode/posts/317/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/317/</guid><description>축에 평행한 직사각형 두 개가 주어질 때, 교집합의 넓이가 양수인지 판단하는 문제. 변이나 꼭짓점만 닿는 것은 겹치지 않는 것으로 본다. easy지만 생각보다 easy하지 않은 문제. 처음엔 한쪽의 끝점이나 꼭짓점이 상대 직사각형 안에 들어가는지로 판단하려 했다. 그런데 한쪽이 다른 쪽을 품는 경우나 십자 모양으로 겹치는 경우를 놓치고, 경계 처리도 계속 …</description><pubDate>Mon, 14 Sep 2026 14:46:30 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem Link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/rectangle-overlap/&quot;&gt;https://leetcode.com/problems/rectangle-overlap/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;축에 평행한 직사각형 두 개가 주어질 때, 교집합의 넓이가 양수인지 판단하는 문제. 변이나 꼭짓점만 닿는 것은 겹치지 않는 것으로 본다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;easy지만 생각보다 easy하지 않은 문제.&lt;/p&gt;
&lt;p&gt;처음엔 한쪽의 끝점이나 꼭짓점이 상대 직사각형 안에 들어가는지로 판단하려 했다. 그런데 한쪽이 다른 쪽을 품는 경우나 십자 모양으로 겹치는 경우를 놓치고, 경계 처리도 계속 어긋났다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;x축과 y축을 1차원으로 따로 보고, 겹치는 경우 대신 안 겹치는 경우를 생각하면 된다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;선분 &lt;code&gt;[a, b)&lt;/code&gt;와 &lt;code&gt;[c, d)&lt;/code&gt;가 안 겹치는 경우는 한쪽이 완전히 왼쪽에 있거나 오른쪽에 있는 두 가지뿐이다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;안 겹친다 = b &amp;#x3C;= c or d &amp;#x3C;= a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;직사각형은 x축이나 y축 중 하나라도 떨어져 있으면 안 겹친다. 두 축의 조건을 &lt;code&gt;or&lt;/code&gt;로 묶고 뒤집으면 된다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def isRectangleOverlap(self, rec1: List[int], rec2: List[int]) -&gt; bool:
        [r1x1, r1y1, r1x2, r1y2] = rec1
        [r2x1, r2y1, r2x2, r2y2] = rec2

        return not ((r1x1 &gt;= r2x2 or r1x2 &amp;#x3C;= r2x1) or
            (r1y1 &gt;= r2y2 or r1y2 &amp;#x3C;= r2y1))
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>easy</category></item><item><title>2265. Count Nodes Equal to Average of Subtree</title><link>https://zeikar.dev/leetcode/posts/316/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/316/</guid><description>이진 트리에서 자기 값이 서브트리 전체 값의 평균(내림)과 같은 노드의 개수를 구하는 문제. 노드마다 서브트리를 다시 돌면 O(n^2)이다. 평균을 구하려면 서브트리의 합과 개수만 있으면 되고, 둘 다 자식 값으로 바로 구할 수 있다. 즉, 후위 순회로 (합, 개수)를 올려주면 한 번만 돌아도 된다. 정답 개수도 같은 튜플에 넣어서 올려주면 따로 변수가 필…</description><pubDate>Sun, 13 Sep 2026 12:15:40 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem Link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/count-nodes-equal-to-average-of-subtree/&quot;&gt;https://leetcode.com/problems/count-nodes-equal-to-average-of-subtree/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;이진 트리에서 자기 값이 서브트리 전체 값의 평균(내림)과 같은 노드의 개수를 구하는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;노드마다 서브트리를 다시 돌면 O(n^2)이다.&lt;br&gt;
평균을 구하려면 서브트리의 합과 개수만 있으면 되고, 둘 다 자식 값으로 바로 구할 수 있다. 즉, &lt;strong&gt;후위 순회로 (합, 개수)를 올려주면&lt;/strong&gt; 한 번만 돌아도 된다.&lt;/p&gt;
&lt;p&gt;정답 개수도 같은 튜플에 넣어서 올려주면 따로 변수가 필요 없다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;search(node) = (서브트리 합, 서브트리 노드 수, 서브트리 안의 정답 개수)
빈 노드는 (0, 0, 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;평균은 문제에서 내림이라고 했고 값이 전부 0 이상이라 &lt;code&gt;//&lt;/code&gt; 쓰면 된다.&lt;/p&gt;
&lt;p&gt;참고로 노드가 최대 1000개라 한쪽으로 쏠린 트리면 재귀 깊이도 1000인데, 파이썬 기본 재귀 한도도 1000이다 (실제로 깊이 996에서 터진다). 통과는 했으니 그런 입력은 없는 듯?&lt;/p&gt;
&lt;p&gt;시간복잡도는 O(n)&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def averageOfSubtree(self, root: TreeNode) -&gt; int:

        def search(node: TreeNode):
            if node == None:
                return (0, 0, 0)

            left = search(node.left)
            right = search(node.right)

            weights = left[0] + right[0] + node.val
            cnt = left[1] + right[1] + 1
            res = left[2] + right[2]

            if weights // cnt == node.val:
                res += 1

            return (weights, cnt, res)

        return search(root)[2]
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>835. Image Overlap</title><link>https://zeikar.dev/leetcode/posts/315/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/315/</guid><description>n x n 이진 행렬 img1을 상하좌우로만 평행이동해서 img2 위에 겹쳤을 때, 둘 다 1인 칸의 최대 개수를 구하는 문제. n이 최대 30이라 평행이동을 전부 해봐도 (2n-1)^2 = 3481번이다. 그냥 다 돌려보면 된다. 처음에는 (3n-1) x (3n-1) 보드를 깔고 img1을 실제로 옮겨 그려서 셌는데 시간 초과가 났다. 이동할 때마다 보드…</description><pubDate>Sun, 13 Sep 2026 03:00:29 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem Link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/image-overlap/&quot;&gt;https://leetcode.com/problems/image-overlap/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;n x n 이진 행렬 img1을 상하좌우로만 평행이동해서 img2 위에 겹쳤을 때, 둘 다 1인 칸의 최대 개수를 구하는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;n이 최대 30이라 평행이동을 전부 해봐도 (2n-1)^2 = 3481번이다. 그냥 다 돌려보면 된다.&lt;/p&gt;
&lt;p&gt;처음에는 (3n-1) x (3n-1) 보드를 깔고 img1을 실제로 옮겨 그려서 셌는데 시간 초과가 났다.&lt;br&gt;
이동할 때마다 보드 7921칸을 0으로 채우는데 실제로 겹치는 영역은 평균 100칸 정도라 거의 다 낭비다. &lt;strong&gt;옮겨 그릴 필요 없이&lt;/strong&gt; &lt;code&gt;img1[x+r][y+c]&lt;/code&gt;랑 &lt;code&gt;img2[x][y]&lt;/code&gt;를 바로 비교하면 된다.&lt;/p&gt;
&lt;p&gt;그래도 x, y를 0부터 n-1까지 다 돌면서 범위 밖을 &lt;code&gt;continue&lt;/code&gt;로 거르니 절반밖에 안 줄었다. 처음부터 &lt;strong&gt;겹치는 범위만&lt;/strong&gt; 돌면 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;img2[x]    를 읽으려면   x 가 0 이상 n 미만
img1[x+r]  를 읽으려면   x 가 -r 이상 n-r 미만
=&gt; range(max(0, -r), min(n, n-r))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여기서 &lt;code&gt;max(r, 0)&lt;/code&gt;으로 잘못 써서 한 번 틀렸다... 파이썬은 음수 인덱스를 에러 없이 뒤에서부터 읽어서 조용히 틀린다.&lt;/p&gt;
&lt;p&gt;n=30 최악 입력에서 0.46s, 0.21s, 0.074s 순으로 줄었다. 시간복잡도는 O(n^4)&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def largestOverlap(self, img1: List[List[int]], img2: List[List[int]]) -&gt; int:
        n = len(img1)
        res = 0

        for r in range(-n, n):
            for c in range(-n, n):
                cnt = 0
                for x in range(max(-r, 0), min(n-r, n)):
                    for y in range(max(-c, 0), min(n-c, n)):
                        cnt += 1 if img1[x+r][y+c] == img2[x][y] and img2[x][y] == 1 else 0

                res = max(res, cnt)

        return res
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>3414. Maximum Score of Non-overlapping Intervals</title><link>https://zeikar.dev/leetcode/posts/314/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/314/</guid><description>구간마다 가중치가 있을 때, 서로 겹치지 않는 구간을 최대 4개까지 골라 가중치 합을 최대로 만드는 문제. 합이 같으면 인덱스 배열이 사전순으로 가장 작은 것을 반환해야 한다. 가중치 있는 구간 스케줄링에 조건이 두 개 붙은 문제. 일단 끝점 기준으로 정렬하면 어떤 구간과 안 겹치는 구간들은 항상 앞쪽에 몰려 있다. 그래서 이분 탐색으로 경계만 찾으면 DP…</description><pubDate>Sat, 12 Sep 2026 12:06:58 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem Link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-score-of-non-overlapping-intervals/&quot;&gt;https://leetcode.com/problems/maximum-score-of-non-overlapping-intervals/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;구간마다 가중치가 있을 때, 서로 겹치지 않는 구간을 최대 4개까지 골라 가중치 합을 최대로 만드는 문제. 합이 같으면 인덱스 배열이 사전순으로 가장 작은 것을 반환해야 한다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;가중치 있는 구간 스케줄링에 조건이 두 개 붙은 문제.&lt;/p&gt;
&lt;p&gt;일단 끝점 기준으로 정렬하면 어떤 구간과 안 겹치는 구간들은 항상 앞쪽에 몰려 있다. 그래서 이분 탐색으로 경계만 찾으면 DP로 풀 수 있다. 경계가 닿아도 겹치는 걸로 치니까 &lt;code&gt;r &amp;#x3C; l&lt;/code&gt;인 것만 세야 한다.&lt;/p&gt;
&lt;p&gt;조건 두 개는 상태를 하나씩 늘려주면 된다.&lt;br&gt;
최대 4개니까 &lt;strong&gt;고른 개수를 한 차원 추가&lt;/strong&gt;하고, 사전순은 점수만 들고 있으면 동점일 때 비교할 수가 없으니 &lt;strong&gt;(점수, 인덱스 리스트)를 같이 저장&lt;/strong&gt;한다. 리스트가 최대 4개라 부담도 없다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dp[c][k] = 앞 k개까지 보고 c개 이하 썼을 때의 (점수, 인덱스 리스트)
dp[c][k+1] = better(dp[c][k], dp[c-1][prev[k]] + 현재 구간)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;인덱스 리스트는 붙일 때마다 정렬해줘야 한다. 끝점 기준으로 정렬했으니 원래 인덱스 순서랑 다를 수 있다 (마지막에 한 번만 정렬하면 중간 비교가 이미 틀린다).&lt;/p&gt;
&lt;p&gt;시간복잡도는 O(n log n)&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def maximumWeight(self, intervals: List[List[int]]) -&gt; List[int]:
        n = len(intervals)
        new_intervals = sorted((r, l, w, i) for i, (l, r, w) in enumerate(intervals))

        prev = []
        for i in range(n):
            prev.append(bisect_left(new_intervals, new_intervals[i][1], key=lambda x: x[0]))

        dp = [[(0, [])] * (n + 1) for _ in range(5)]

        for i in range(n):
            for c in range(1, 5):
                a = dp[c][i]
                b = dp[c-1][prev[i]]
                score = b[0]+new_intervals[i][2]
                itv = sorted(b[1] + [new_intervals[i][3]])

                if a[0] &gt; score or (a[0] == score and a[1] &amp;#x3C; itv):
                    dp[c][i+1] = a
                else:
                    dp[c][i+1] = (b[0]+new_intervals[i][2], itv)

        return dp[4][n][1]
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>hard</category></item><item><title>3871. Count Commas in Range II</title><link>https://zeikar.dev/leetcode/posts/313/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/313/</guid><description>1부터 n까지의 수를 세 자리마다 콤마를 찍어서 쓸 때, 콤마가 총 몇 개 쓰이는지 구하는 문제. n이 10^15까지라 하나씩 세는 건 불가능하다. 잘 생각해보면 1000 이상인 수는 콤마가 최소 1개, 10^6 이상이면 최소 2개... 이런 식이다. 즉, 1000의 거듭제곱마다 그 이상인 수의 개수를 더해주면 된다. n 이하에서 div 이상인 수는 n -…</description><pubDate>Sat, 12 Sep 2026 10:26:48 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/count-commas-in-range-ii/&quot;&gt;https://leetcode.com/problems/count-commas-in-range-ii/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;1부터 n까지의 수를 세 자리마다 콤마를 찍어서 쓸 때, 콤마가 총 몇 개 쓰이는지 구하는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;n이 10^15까지라 하나씩 세는 건 불가능하다.&lt;/p&gt;
&lt;p&gt;잘 생각해보면 1000 이상인 수는 콤마가 최소 1개, 10^6 이상이면 최소 2개... 이런 식이다.&lt;br&gt;
즉, &lt;strong&gt;1000의 거듭제곱마다 그 이상인 수의 개수&lt;/strong&gt;를 더해주면 된다. n 이하에서 div 이상인 수는 &lt;code&gt;n - div + 1&lt;/code&gt;개.&lt;/p&gt;
&lt;p&gt;10^15까지라 루프는 5번만 돈다. 시간복잡도는 O(log n)&lt;/p&gt;
&lt;p&gt;C++로 풀었던 걸 파이썬으로 옮긴 것. C++에서는 오버플로 때문에 &lt;code&gt;unsigned long long&lt;/code&gt;을 썼는데 파이썬은 정수 범위 제한이 없어서 그냥 옮기면 된다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def countCommas(self, n: int) -&gt; int:
        res = 0
        div = 1000
        while n &gt;= div:
            res += n - div + 1
            div *= 1000

        return res
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>2461. Maximum Sum of Distinct Subarrays With Length K</title><link>https://zeikar.dev/leetcode/posts/312/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/312/</guid><description>k 길이의 부분 배열 중에 원소가 전부 다를 때의 합의 최댓값을 구하는 문제 간단한 슬라이딩 윈도우 문제. 원소 하나씩 보면서 counter 하면서 모두 distinct 이면 결과에 업데이트 해주면 된다.</description><pubDate>Sun, 20 Apr 2025 01:00:43 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/maximum-sum-of-distinct-subarrays-with-length-k/&quot;&gt;https://leetcode.com/problems/maximum-sum-of-distinct-subarrays-with-length-k/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;k 길이의 부분 배열 중에 원소가 전부 다를 때의 합의 최댓값을 구하는 문제&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;간단한 슬라이딩 윈도우 문제.&lt;br&gt;
원소 하나씩 보면서 counter 하면서 모두 distinct 이면 결과에 업데이트 해주면 된다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def maximumSubarraySum(self, nums: List[int], k: int) -&gt; int:
        result = 0
        c = Counter(nums[0 : k - 1])
        s = sum(nums[0 : k - 1])

        for i in range(k - 1, len(nums)):
            s += nums[i]
            c[nums[i]] += 1

            if len(c) == k:
                result = max(result, s)

            if c[nums[i - k + 1]] == 1:
                del c[nums[i - k + 1]]
            else:
                c[nums[i - k + 1]] -= 1
            s -= nums[i - k + 1]

        return result

&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>129. Sum Root to Leaf Numbers</title><link>https://zeikar.dev/leetcode/posts/311/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/311/</guid><description>루트부터 리프 노드까지를 이었을 때 생기는 수를 다 더하는 문제 재귀적으로 구현하면 된다. 자식 노드로 내려갈 때 현재 값에 10을 계속 곱해주면서 더하면 된다. 보통 재귀적으로 합을 구할 때와 순서가 반대라서 약간 헷갈리긴 한다</description><pubDate>Sun, 13 Apr 2025 01:23:30 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/sum-root-to-leaf-numbers&quot;&gt;https://leetcode.com/problems/sum-root-to-leaf-numbers&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;루트부터 리프 노드까지를 이었을 때 생기는 수를 다 더하는 문제&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;재귀적으로 구현하면 된다. &lt;strong&gt;자식 노드로 내려갈 때 현재 값에 10을 계속 곱해주면서 더하면&lt;/strong&gt; 된다.&lt;br&gt;
보통 재귀적으로 합을 구할 때와 순서가 반대라서 약간 헷갈리긴 한다&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def sumNumbers(self, root: Optional[TreeNode]) -&gt; int:

        def getSum(node, s):
            if not node:
                return 0
            if not node.left and not node.right:
                return s * 10 + node.val

            return getSum(node.left, s * 10 + node.val) + getSum(
                node.right, s * 10 + node.val
            )

        return getSum(root, 0)

&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>103. Binary Tree Zigzag Level Order Traversal</title><link>https://zeikar.dev/leetcode/posts/310/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/310/</guid><description>이진 트리가 주어질 때 레벨 별로 지그재그 하면서 출력하는 문제 레벨별로 맵을 만들고 순서에 맞게 집어넣으면 된다. 일반 bfs 탐색을 약간 변형하면 됨</description><pubDate>Sun, 06 Apr 2025 00:18:41 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/&quot;&gt;https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;이진 트리가 주어질 때 레벨 별로 지그재그 하면서 출력하는 문제&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;레벨별로 맵을 만들고 순서에 맞게 집어넣으면 된다. 일반 bfs 탐색을 약간 변형하면 됨&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def zigzagLevelOrder(self, root: Optional[TreeNode]) -&gt; List[List[int]]:
        queue = deque()
        if root:
            queue.append([root, 0])
        result = defaultdict(deque)

        while queue:
            c = queue.popleft()
            [node, level] = c
            if level % 2 == 0:
                result[level].append(node.val)
            else:
                result[level].appendleft(node.val)

            if node.left:
                queue.append([node.left, level + 1])
            if node.right:
                queue.append([node.right, level + 1])

        return [list(v) for v in result.values()]

&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>433. Minimum Genetic Mutation</title><link>https://zeikar.dev/leetcode/posts/309/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/309/</guid><description>ACGT로만 이루어진 gene이 둘 주어질 때 startGene 에서 endGene으로 mutate가 가능하면 최소 횟수를 출력하는 문제. mutate는 bank에 있는 문자열로만 가능하다. bank 개수 제한이 아주 여유로워서 DFS, BFS 다 가능하다. 일단 간단하게 DFS 백트래킹으로 구현했다. bank로 mutate가 가능한지 판단해서 이동하는 방…</description><pubDate>Sun, 30 Mar 2025 04:12:32 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/minimum-genetic-mutation/&quot;&gt;https://leetcode.com/problems/minimum-genetic-mutation/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;ACGT로만 이루어진 gene이 둘 주어질 때 startGene 에서 endGene으로 mutate가 가능하면 최소 횟수를 출력하는 문제. mutate는 bank에 있는 문자열로만 가능하다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;bank 개수 제한이 아주 여유로워서 &lt;strong&gt;DFS, BFS 다 가능&lt;/strong&gt;하다.&lt;br&gt;
일단 간단하게 DFS 백트래킹으로 구현했다. bank로 mutate가 가능한지 판단해서 이동하는 방식.&lt;/p&gt;
&lt;p&gt;최단 개수이므로 BFS가 정석이긴 하다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def minMutation(self, startGene: str, endGene: str, bank: List[str]) -&gt; int:        
        visited = defaultdict(bool)

        # 한글자만 다른지 체크
        def canMutate(gene, newGene):
            diff = 0
            for i in range(len(gene)):
                if gene[i] != newGene[i]:
                    diff += 1
            return diff == 1

        def solve(gene):
            if gene == endGene:
                return 0
            
            ret = float(&apos;inf&apos;)
            for b in bank:
                if visited[b]:
                    continue

                if canMutate(gene, b):
                    visited[b] = True
                    ret = min(ret, solve(b) + 1)
                    visited[b] = False
                        
            return ret

        result = solve(startGene)
        if result == float(&apos;inf&apos;):
            return -1
        return result
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>215. Kth Largest Element in an Array</title><link>https://zeikar.dev/leetcode/posts/308/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/308/</guid><description>배열에서 정렬 없이 k 번째 큰 수를 찾는 문제 정렬하면 쉽지만 안하고 풀어야 한다. quick sort를 응용한 quick select 알고리즘을 쓰면 된다. quick sort와 비슷하게 피벗을 잡고 피벗 기준으로 왼쪽은 더 큰 값들, 오른쪽에는 더 작은 값들을 몰아 넣는다. 여기서 포인트는 quick select는 정렬이 필요가 없으므로 왼쪽, 오른쪽…</description><pubDate>Sun, 23 Mar 2025 00:37:43 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/kth-largest-element-in-an-array/&quot;&gt;https://leetcode.com/problems/kth-largest-element-in-an-array/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;배열에서 &lt;strong&gt;정렬 없이&lt;/strong&gt; k 번째 큰 수를 찾는 문제&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;정렬하면 쉽지만 안하고 풀어야 한다. quick sort를 응용한 &lt;strong&gt;quick select&lt;/strong&gt; 알고리즘을 쓰면 된다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;quick sort와 비슷하게 피벗&lt;/strong&gt;을 잡고 피벗 기준으로 &lt;strong&gt;왼쪽은 더 큰 값들, 오른쪽에는 더 작은 값&lt;/strong&gt;들을 몰아 넣는다. 여기서 포인트는 quick select는 정렬이 필요가 없으므로 왼쪽, 오른쪽 기준으로 한 덩어리만 체크해주면 된다.&lt;br&gt;
k가 왼쪽 배열보다 작다면 왼쪽 배열 내에서 k 번째 수를 찾으면 된다.&lt;/p&gt;
&lt;p&gt;n + n/2 + n/4 + ... 으로 줄어서 시간 복잡도는 O(n). 물론 운이 아주 나쁘다면 O(n^2)겠지만 평균은 O(n) 이다.&lt;/p&gt;
&lt;p&gt;메모리 제한은 따로 없으므로 구현이 간단하게 배열 복사 방식으로 구현하면 좋다. (in-place로 배열 원소 교체도 가능한데 코드가 꽤 지저분하다)&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def findKthLargest(self, nums: List[int], k: int) -&gt; int:
        def quickSelect(arr, n):
            if len(arr) == 1:
                return arr[0]
            
            pivot = arr[random.randint(0, len(arr)-1)]
            left = [x for x in arr if x &gt; pivot]
            mid = [x for x in arr if x == pivot]
            right = [x for x in arr if x &amp;#x3C; pivot]

            if n &amp;#x3C; len(left):
                return quickSelect(left, n)
            elif n &amp;#x3C; len(left) + len(mid):
                return pivot
            else:
                return quickSelect(right, n - len(left) - len(mid))
        
        return quickSelect(nums, k-1)

        
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>71. Simplify Path</title><link>https://zeikar.dev/leetcode/posts/307/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/307/</guid><description>unix path가 주어질 때 간단화 하는 문제. 즉, 여러 슬래시나 .. 같은 디렉토리 변경 등을 다 처리후 최소화된 경로로 만드는 문제. 스택을 쓰면 되는 간단한 문제. 먼저 슬래시 기준으로 split을 해준다. 일반적인 ., .. 같은 디렉토리 명렁어가 아닌 경우 스택에 넣어주고, ..의 경우에는 스택에서 빼주면 된다. 비어있는 문자열은 연속된 슬래시…</description><pubDate>Sun, 16 Mar 2025 04:34:06 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/simplify-path/&quot;&gt;https://leetcode.com/problems/simplify-path/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;unix path가 주어질 때 간단화 하는 문제.&lt;br&gt;
즉, 여러 슬래시나 .. 같은 디렉토리 변경 등을 다 처리후 최소화된 경로로 만드는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;스택을 쓰면 되는 간단한 문제.&lt;/p&gt;
&lt;p&gt;먼저 슬래시 기준으로 split을 해준다. 일반적인 &lt;code&gt;.&lt;/code&gt;, &lt;code&gt;..&lt;/code&gt; 같은 디렉토리 명렁어가 아닌 경우 스택에 넣어주고, &lt;code&gt;..&lt;/code&gt;의 경우에는 스택에서 빼주면 된다. 비어있는 문자열은 연속된 슬래시이므로 무시하면 된다.&lt;/p&gt;
&lt;p&gt;마지막으로 슬래시로 시작되어야 하므로 &lt;code&gt;/&lt;/code&gt;를 맨 앞에 넣고 리턴하면 된다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def simplifyPath(self, path: str) -&gt; str:
        stack = []
        split = path.split(&apos;/&apos;)
        for i in range(len(split)):
            s = split[i]

            if s == &apos;.&apos;:
                continue
            elif s == &apos;..&apos;:
                if len(stack) &gt;= 1:
                    stack.pop()
            elif s != &apos;&apos;:
                stack.append(s)

        return &apos;/&apos; + &apos;/&apos;.join(stack)

&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>131. Palindrome Partitioning</title><link>https://zeikar.dev/leetcode/posts/306/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/306/</guid><description>문자열이 주어질 때 적당히 잘라서 모든 substring이 팰린드롬이 되게 배열을 반환하는 문제. 백트래킹으로 하면 간단하다. 애초에 모든 substring 경우의 수를 반환해야 해서 다 탐색할 수밖에 없다. 다만 그냥 하면 중복되는 substring 체크가 많기 때문에 dp를 섞어주면 빠르게 뽑을 수 있다. 시간 복잡도는 O(2^N * N) 공간 복잡도는…</description><pubDate>Sun, 16 Feb 2025 01:40:14 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/palindrome-partitioning/&quot;&gt;https://leetcode.com/problems/palindrome-partitioning/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;문자열이 주어질 때 적당히 잘라서 모든 substring이 팰린드롬이 되게 배열을 반환하는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;백트래킹으로 하면 간단하다. 애초에 모든 substring 경우의 수를 반환해야 해서 다 탐색할 수밖에 없다.&lt;br&gt;
다만 그냥 하면 중복되는 substring 체크가 많기 때문에 dp를 섞어주면 빠르게 뽑을 수 있다.&lt;/p&gt;
&lt;p&gt;시간 복잡도는 O(2^N * N)&lt;br&gt;
공간 복잡도는 O(2^N)&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def partition(self, s: str) -&gt; List[List[str]]:
        @cache
        def is_palindrome(x, y):
            for i in range(y-x):
                if s[x+i] != s[y-i-1]:
                    return False
            return True

        @cache
        def check(idx):
            if idx == len(s):
                return [[]]

            ret = []
            for i in range(idx, len(s)):
                substr = s[idx:i+1]

                if is_palindrome(idx, i + 1):
                    palindrome = check(i+1)

                    for p in palindrome:
                        ret.append([substr] + p)

            return ret

        return check(0)

&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>128. Longest Consecutive Sequence</title><link>https://zeikar.dev/leetcode/posts/305/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/305/</guid><description>배열이 주어질 때 가장 긴 원소들의 연속 증가 길이를 구하는 문제 (순서는 상관 없음) O(n log n) 풀이는 정렬하면 되니까 쉽다. **O(n)**은 약간 생각해야 함. 일단 O(n)이니까 hashmap 방식으로 가야하고 hashmap으로 구현하려면 각 원소들을 한 번씩만 순회해야 한다. 원소들을 set에 넣어두고 set의 처음 지점인 원소부터 쭉 올…</description><pubDate>Sat, 08 Feb 2025 10:45:47 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/longest-consecutive-sequence/&quot;&gt;https://leetcode.com/problems/longest-consecutive-sequence/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;배열이 주어질 때 가장 긴 원소들의 연속 증가 길이를 구하는 문제 (순서는 상관 없음)&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;O(n log n) 풀이는 정렬하면 되니까 쉽다. **O(n)**은 약간 생각해야 함.&lt;br&gt;
일단 O(n)이니까 hashmap 방식으로 가야하고 hashmap으로 구현하려면 각 원소들을 한 번씩만 순회해야 한다.&lt;/p&gt;
&lt;p&gt;원소들을 set에 넣어두고 set의 처음 지점인 원소부터 쭉 올라가면서 길이를 재주면 된다.&lt;/p&gt;
&lt;p&gt;처음에는 dict에 넣고 배열을 순회하면서 처음인지 판단했는데 이럴 경우 visited가 추가로 필요하다. &lt;strong&gt;순회 자체도 set에서부터 하는 것이 편함&lt;/strong&gt;!&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def longestConsecutive(self, nums: List[int]) -&gt; int:
        s = set(nums)
        answer = 0
        
        for n in s:
            if n-1 in s:
                continue
            
            l = 0
            next = n
            while next in s:
                l += 1
                next += 1
            answer = max(answer, l)

        return answer
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>1514. Path with Maximum Probability</title><link>https://zeikar.dev/leetcode/posts/304/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/304/</guid><description>간선에 확률이 있는 그래프가 주어진다. 시작점에서 끝점으로 갈 때 확률의 최대를 구하는 문제. 그냥 다익스트라를 돌려주면 된다. 일반 다익스트라가 합이 최소가 되게 한다면 여기서는 곱이 최대가 되게 뽑아주면 된다.</description><pubDate>Sat, 31 Aug 2024 08:32:47 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/path-with-maximum-probability/&quot;&gt;https://leetcode.com/problems/path-with-maximum-probability/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;간선에 확률이 있는 그래프가 주어진다. 시작점에서 끝점으로 갈 때 확률의 최대를 구하는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;그냥 다익스트라를 돌려주면 된다.&lt;/p&gt;
&lt;p&gt;일반 다익스트라가 합이 최소가 되게 한다면 여기서는 곱이 최대가 되게 뽑아주면 된다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -&gt; float:
        graph = defaultdict(list)
        for i in range(len(edges)):
            graph[edges[i][0]].append((edges[i][1], succProb[i]))
            graph[edges[i][1]].append((edges[i][0], succProb[i]))
        
        prob = [0] * n
        prob[start_node] = 1
        pq = [(-1, start_node)]
        while len(pq) &gt; 0:
            current = heapq.heappop(pq)
            (p, c) = current
            p *= -1
            if c == end_node:
                return p

            for g in graph[c]:
                next = g[0]
                np = p * g[1]

                if prob[next] &amp;#x3C; np:
                    heapq.heappush(pq, (-np, next))
                    prob[next] = np

        return 0.0
&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>592. Fraction Addition and Subtraction</title><link>https://zeikar.dev/leetcode/posts/303/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/303/</guid><description>분수들의 식이 주어질 때 계산하는 문제 스트링 파싱 후 계산하면 된다. 초등학교 때 배운 분수의 덧셈 그대로 구현하면 되는데 분모는 최소공배수로 맞추고 분자끼리 더한 후 최대공약수로 분자와 분모를 각각 나눠주면 된다. 정규표현식으로 파싱이 깔끔하게 되는거 같은데 난 무식하게... 하나씩 파싱했다 ㅋㅋ</description><pubDate>Fri, 23 Aug 2024 12:26:36 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/fraction-addition-and-subtraction/&quot;&gt;https://leetcode.com/problems/fraction-addition-and-subtraction/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;분수들의 식이 주어질 때 계산하는 문제&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;스트링 파싱 후 계산하면 된다.&lt;br&gt;
초등학교 때 배운 분수의 덧셈 그대로 구현하면 되는데 분모는 최소공배수로 맞추고 분자끼리 더한 후 최대공약수로 분자와 분모를 각각 나눠주면 된다.&lt;/p&gt;
&lt;p&gt;정규표현식으로 파싱이 깔끔하게 되는거 같은데 난 무식하게... 하나씩 파싱했다 ㅋㅋ&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def fractionAddition(self, expression: str) -&gt; str:
        sign = 1
        n = 0
        numerators, denominators = [], []
        if expression[0] == &quot;-&quot;:
            sign = -1
            expression = expression[1:]

        for e in expression:
            if e == &quot;-&quot; or e == &quot;+&quot;:
                denominators.append(n)
                n = 0

                if e == &quot;-&quot;:
                    sign = -1
                else:
                    sign = 1
            elif e == &quot;/&quot;:
                numerators.append(sign * n)
                n = 0
            else:
                n *= 10
                n += int(e)
        denominators.append(n)

        denominator = 1
        g = 1
        for d in denominators:
            g = gcd(g, d)
            denominator *= d
        denominator //= g

        numerator = 0
        for i in range(len(numerators)):
            numerator += numerators[i] * (denominator // denominators[i])
        g = gcd(numerator, denominator)
        return f&quot;{numerator//g}/{denominator//g}&quot;

&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>medium</category></item><item><title>476. Number Complement</title><link>https://zeikar.dev/leetcode/posts/302/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/302/</guid><description>수가 주어질 때 complement를 구하는 문제. 1이 0이 되고 0이 1이 되려면?? XOR를 쓰면 된다. 자릿수를 올라가면서 1과 XOR를 하면 1은 0이 되고 0은 1이 된다.</description><pubDate>Thu, 22 Aug 2024 12:00:09 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/number-complement/&quot;&gt;https://leetcode.com/problems/number-complement/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;수가 주어질 때 complement를 구하는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;1이 0이 되고 0이 1이 되려면?? &lt;strong&gt;XOR&lt;/strong&gt;를 쓰면 된다.&lt;br&gt;
자릿수를 올라가면서 1과 XOR를 하면 1은 0이 되고 0은 1이 된다.&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def findComplement(self, num: int) -&gt; int:
        b = 1
        while num &gt;= b:
            num ^= b
            b &amp;#x3C;&amp;#x3C;= 1
        return num

&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>easy</category></item><item><title>664. Strange Printer</title><link>https://zeikar.dev/leetcode/posts/301/</link><guid isPermaLink="true">https://zeikar.dev/leetcode/posts/301/</guid><description>한 문자를 죽 이어 붙이거나 특정 범위의 문자열을 교체하는 2가지 연산을 할 때 최소한의 연산으로 문자열을 만드는 횟수를 찾는 문제. 약간 분할 정복, 완전탐색 느낌이 나는데 쉽지 않다. 일단 양옆이 같은 문자인 경우 중간을 바꿔치기 했다고 생각할 수 있다. 그 외의 경우는 이어 붙여야 한다. 따라서 문제를 나눠볼 수 있는데 맨 앞과 같은 문자가 중간에 나…</description><pubDate>Wed, 21 Aug 2024 12:43:39 GMT</pubDate><content:encoded>&lt;h1 id=&quot;user-content-problem-link&quot;&gt;Problem link&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode.com/problems/strange-printer/&quot;&gt;https://leetcode.com/problems/strange-printer/&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&quot;user-content-problem-summary&quot;&gt;Problem Summary&lt;/h1&gt;
&lt;p&gt;한 문자를 죽 이어 붙이거나 특정 범위의 문자열을 교체하는 2가지 연산을 할 때 최소한의 연산으로 문자열을 만드는 횟수를 찾는 문제.&lt;/p&gt;
&lt;h1 id=&quot;user-content-solution&quot;&gt;Solution&lt;/h1&gt;
&lt;p&gt;약간 분할 정복, 완전탐색 느낌이 나는데 쉽지 않다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/6c0cc068-7a8c-4050-8847-2c24ce7fcda0&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;일단 양옆이 같은 문자인 경우 중간을 바꿔치기 했다고 생각할 수 있다. 그 외의 경우는 이어 붙여야 한다.&lt;br&gt;
따라서 문제를 나눠볼 수 있는데 &lt;strong&gt;맨 앞과 같은 문자가 중간에 나온 경우 처음부터 거기까지를 나누고 그 다음부분으로 나눌&lt;/strong&gt; 수 있다.&lt;br&gt;
그 외의 경우 그냥 무식하게 붙이는 수밖에 없다.&lt;/p&gt;
&lt;p&gt;인덱스로 cache를 하면 (시작과 끝 인덱스) 공간복잡도를 줄일 수 있지만 가독성은 이게 좋으므로 이렇게 놔두었다.&lt;/p&gt;
&lt;p&gt;뭔가 잘 돌아가는데 왜 잘 돌아가는지 모르겠다...&lt;/p&gt;
&lt;h1 id=&quot;user-content-source-code&quot;&gt;Source Code&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-Python&quot;&gt;class Solution:
    def strangePrinter(self, s: str) -&gt; int:

        @cache
        def dfs(s):
            if len(s) == 0:
                return 0

            ret = 1 + dfs(s[1:])

            for i in range(1, len(s)):
                if s[i] == s[0]:
                    ret = min(ret, dfs(s[1:i]) + dfs(s[i:]))

            return ret

        return dfs(s)

&lt;/code&gt;&lt;/pre&gt;</content:encoded><category>hard</category></item></channel></rss>