<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Two Pointers on Kelly Dev</title><link>https://kelly-chui.github.io/algorithmtags/two-pointers/</link><description>Recent content in Two Pointers on Kelly Dev</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 14 Aug 2026 11:53:07 +0900</lastBuildDate><atom:link href="https://kelly-chui.github.io/algorithmtags/two-pointers/index.xml" rel="self" type="application/rss+xml"/><item><title>Leetcode 3090. Maximum Length Substring With Two Occurrences</title><link>https://kelly-chui.github.io/ps/2026/ps-leetcode-3090-maximum-length-substring-with-two-occurrences/</link><pubDate>Fri, 14 Aug 2026 11:53:07 +0900</pubDate><guid>https://kelly-chui.github.io/ps/2026/ps-leetcode-3090-maximum-length-substring-with-two-occurrences/</guid><description>&lt;h2 id="문제"&gt;문제&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://leetcode.com/problems/maximum-length-substring-with-two-occurrences"&gt;https://leetcode.com/problems/maximum-length-substring-with-two-occurrences&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="풀이"&gt;풀이&lt;/h2&gt;
&lt;p&gt;스트링 &lt;code&gt;s&lt;/code&gt;가 주어지고, 스트링 &lt;code&gt;s&lt;/code&gt;의 서브스트링 중에서 같은 문자가 최대 2번 까지만 등장하는 서브스트링의 최대 길이를 리턴하는 문제이다.&lt;/p&gt;
&lt;p&gt;투 포인터를 이용해서, 서브스트링 내부의 각 문자의 개수는 &lt;code&gt;frequencies&lt;/code&gt; 딕셔너리로 추적하고, 같은 문자가 2개를 초과하면 &lt;code&gt;start&lt;/code&gt;, 그렇지 않다면 &lt;code&gt;end&lt;/code&gt;를 증가시키는 방향으로 &lt;code&gt;s&lt;/code&gt;를 탐색하면 된다.&lt;/p&gt;
&lt;p&gt;문제의 제약조건이 널널해서 $O(n^2)$ 방식의 브루트 포스로도 풀 수 있지만 투 포인터를 이용하면 $O(n)$으로 쉽게 풀 수 있다.&lt;/p&gt;
&lt;h2 id="코드"&gt;코드&lt;/h2&gt;
&lt;div class="code-theme-github"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;maximumLengthSubstring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;frequencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;frequencies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frequencies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;frequencies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;frequencies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</description></item><item><title>LeetCode 2958. Length of Longest Subarray With at Most K Frequency</title><link>https://kelly-chui.github.io/ps/2026/ps-leetcode-2958-length-of-longest-subarray-with-at-most-k-frequency/</link><pubDate>Wed, 12 Aug 2026 21:14:37 +0900</pubDate><guid>https://kelly-chui.github.io/ps/2026/ps-leetcode-2958-length-of-longest-subarray-with-at-most-k-frequency/</guid><description>&lt;h2 id="문제"&gt;문제&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://leetcode.com/problems/length-of-longest-subarray-with-at-most-k-frequency"&gt;https://leetcode.com/problems/length-of-longest-subarray-with-at-most-k-frequency&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="풀이"&gt;풀이&lt;/h2&gt;
&lt;p&gt;정수 배열 &lt;code&gt;nums&lt;/code&gt;와 정수 &lt;code&gt;k&lt;/code&gt;가 주어질 때, 어떤 숫자도 &lt;code&gt;k&lt;/code&gt;번보다 많이 등장하지 않는 가장 긴 연속 부분 배열의 길이를 구하면 된다.&lt;/p&gt;
&lt;p&gt;연속 부분 배열이므로 슬라이딩 윈도우를 사용했다. &lt;code&gt;start&lt;/code&gt;부터 &lt;code&gt;end&lt;/code&gt;까지를 현재 윈도우로 두고, &lt;code&gt;end&lt;/code&gt;를 오른쪽으로 이동하면서 숫자별 등장 횟수를 갱신한다.&lt;/p&gt;
&lt;p&gt;새로 추가한 숫자의 등장 횟수가 &lt;code&gt;k&lt;/code&gt;를 초과하면 조건을 만족할 때까지 &lt;code&gt;start&lt;/code&gt;를 오른쪽으로 이동한다. 이때 윈도우의 가장 왼쪽 숫자부터 하나씩 제거하면 된다. 윈도우가 조건을 만족하는 상태가 되면 현재 길이로 정답을 갱신한다.&lt;/p&gt;</description></item><item><title>Codeforces 451B. Sort the Array</title><link>https://kelly-chui.github.io/ps/2026/ps-codeforces-451b-sort-the-array/</link><pubDate>Wed, 05 Aug 2026 21:03:54 +0900</pubDate><guid>https://kelly-chui.github.io/ps/2026/ps-codeforces-451b-sort-the-array/</guid><description>&lt;h2 id="문제"&gt;문제&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://codeforces.com/problemset/problem/451/B"&gt;https://codeforces.com/problemset/problem/451/B&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="풀이"&gt;풀이&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;n&lt;/code&gt; 크기 수열 &lt;code&gt;a&lt;/code&gt;가 주어진다. 이 수열의 특정 구간을 선택해서 뒤집는 것을 1번 했을 때, 이 수열이 오름차순으로 정렬되어 있다면 &lt;code&gt;&amp;quot;yes&amp;quot;&lt;/code&gt;와 그 구간을, 불가능하면 &lt;code&gt;&amp;quot;no&amp;quot;&lt;/code&gt;를 출력하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;n&lt;/code&gt;의 제약 조건이 $1 &amp;lt;= n &amp;lt;= 10^5$ 이기 때문에, 브루트 포스로는 시간 내에 풀기 힘들다.&lt;/p&gt;
&lt;p&gt;특정 구간을 선택해서 뒤집는 행동을 1번만 한다는 것이 힌트인데, 구간 하나를 1번 뒤집어서 수열 전체가 오름차순이 된다는 것은, 수열의 일부 구간만 내림차순 이라는 것이다.&lt;/p&gt;</description></item><item><title>Codeforces 279B. Books</title><link>https://kelly-chui.github.io/ps/2026/ps-codeforces-279b-books/</link><pubDate>Thu, 30 Jul 2026 23:08:53 +0900</pubDate><guid>https://kelly-chui.github.io/ps/2026/ps-codeforces-279b-books/</guid><description>&lt;h2 id="문제"&gt;문제&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://codeforces.com/problemset/problem/279/B"&gt;https://codeforces.com/problemset/problem/279/B&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="풀이"&gt;풀이&lt;/h2&gt;
&lt;p&gt;책을 읽는데 걸리는 시간이 담긴 크기 &lt;code&gt;n&lt;/code&gt; 배열 &lt;code&gt;a&lt;/code&gt;가 주어지고, 책을 읽을 수 있는 시간 &lt;code&gt;t&lt;/code&gt;가 주어진다. 특정 인덱스를 정해서 그 인덱스부터 책을 순서대로 읽었을때, 최대로 읽을 수 있는 책의 개수를 출력하면된다.&lt;/p&gt;
&lt;p&gt;모든 경우의 수를 탐색하려면 $O(n^2)$의 시간 복잡도를 가지는데 책의 개수가 10만개 이므로, 시간 내로 해결하기는 힘들다.&lt;/p&gt;
&lt;p&gt;&amp;lsquo;순서대로&amp;rsquo;라는 조건이 있으므로, 투 포인터를 이용하면 쉽게 풀 수 있다.&lt;/p&gt;
&lt;p&gt;단 배열 &lt;code&gt;a&lt;/code&gt;의 원소의 크기가 &lt;code&gt;t&lt;/code&gt;보다 클 수 있으므로, 단 한권의 책도 읽지 못하는 경우가 있다. 그래서 책을 0권 읽는 경우의 수도 생각해서 코드를 짜야 한다.&lt;/p&gt;</description></item><item><title>Leetcode 3867. Sum of GCD of Formed Pairs</title><link>https://kelly-chui.github.io/ps/2026/ps-leetcode-3867-sum-of-gcd-of-formed-pairs/</link><pubDate>Thu, 16 Jul 2026 10:29:15 +0900</pubDate><guid>https://kelly-chui.github.io/ps/2026/ps-leetcode-3867-sum-of-gcd-of-formed-pairs/</guid><description>&lt;h2 id="문제"&gt;문제&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://leetcode.com/problems/sum-of-gcd-of-formed-pairs"&gt;https://leetcode.com/problems/sum-of-gcd-of-formed-pairs&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="풀이"&gt;풀이&lt;/h2&gt;
&lt;p&gt;문제에서 지시한 내용을 그대로 구현하면 되는 문제다. 문제의 지시사항은 다음과 같다:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;인티저 배열 &lt;code&gt;nums&lt;/code&gt;의 누적 최대값 배열 &lt;code&gt;mx&lt;/code&gt;를 만든다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefixGcd[i] = gcd(nums[i], mx[i])&lt;/code&gt; 으로 이루어진 배열을 만든다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefixGcd&lt;/code&gt;를 오름차순 정렬한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefixGcd&lt;/code&gt;의 제일 큰 값과 제일 작은 값의 쌍을 만들어, GCD를 구하는 것을 더 이상 만들지 못할 때 까지 반복한다. 만약 &lt;code&gt;prefixGcd&lt;/code&gt;의 원소의 개수가 홀수라서 페어을 만들지 못한 하나가 남는다면 무시한다.&lt;/li&gt;
&lt;li&gt;&amp;lsquo;4&amp;rsquo;에서 구한 모든 GCD 값의 합을 리턴한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;페어을 만드는건 간단한 형태의 투 포인터로 해결했다. 정렬과 유클리드 호제법을 제외하곤 모두 $O(n)$에 해결되기 때문에, 시간복잡도는 $O(n \log n + n \log V) = O(n \log (nV))$이다.&lt;/p&gt;</description></item><item><title>BOJ 13144. list of unique numbers</title><link>https://kelly-chui.github.io/ps/2024/ps-boj-13144-list-of-unique-numbers/</link><pubDate>Wed, 12 Jun 2024 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/ps/2024/ps-boj-13144-list-of-unique-numbers/</guid><description>&lt;h2 id="문제"&gt;문제&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.acmicpc.net/problem/13144"&gt;https://www.acmicpc.net/problem/13144&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="풀이"&gt;풀이&lt;/h2&gt;
&lt;p&gt;매우 특이한 유형의 투 포인터 문제다. &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt;가 증가만 해서는 모든 경우의 수를 나타낼 수 없으며, 모든 경우의 수를 탐색하려면 O(n^2)이 된다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;1 2 3 1 2&lt;/code&gt; 라는 수열이 있을 때를 생각해보자&lt;/p&gt;
&lt;p&gt;겹치는 원소가 나오지 않도록 작성한 일반적인 투 포인터는 1, 12, 123, 231, 312 이렇게 5번 탐색을 하고 종료한다. 하지만 1 2 3 1 2의 정답은 5가 아니라 15이다. 하지만 이 5번의 탐색만으로도 15라는 결과를 얻을 수 있는데, 우리는 정확한 부분 수열의 형태보다 경우의 수만 알면 되기 때문이다.&lt;/p&gt;</description></item></channel></rss>