<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>이 정도면 괜찮다고 말하지 않는다</title>
    <link>https://kazy.tistory.com/</link>
    <description>&amp;quot;이 정도면 괜찮지&amp;quot;라는 말은 소프트웨어의 문제점을 외면하고 포기하는 것이다.</description>
    <language>ko</language>
    <pubDate>Fri, 24 Jul 2026 06:28:03 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>코딩몬</managingEditor>
    <image>
      <title>이 정도면 괜찮다고 말하지 않는다</title>
      <url>https://tistory1.daumcdn.net/tistory/5678432/attach/fcd345d3961745abaffcb359a0d5a5e1</url>
      <link>https://kazy.tistory.com</link>
    </image>
    <item>
      <title>문자열 검색 (String searching) 이야기</title>
      <link>https://kazy.tistory.com/3</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이야기에 앞서&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이번 이야기는 &lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/String-searching_algorithm&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;문자열 검색&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/String-searching_algorithm&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt; (String search)&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;입니다.&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;기본적인 개념은 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;주어진 문자열 (text)에서 특정 문자 (pattern)의 등장 위치&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 찾는 것으로 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;i&gt;문자열 검색, 문자열 탐색, String Matching&amp;nbsp; 그리고 String Searching&lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 등 다양한 이름으로 불리지만 모두 동일한 의미를 갖고 있습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;문자열 검색 알고리즘은 컴퓨터 과학에서 매우 중요한 주제 중 하나로 텍스트 에디터, 검색 엔진, 데이터 압축, 문자열 인코딩 등 다양한 분야에서 사용됩니다. 저희가 문서 작업에서 자주 사용하는 기능인 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;찾기 (CTRL+F)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;가 이러한 문자열 검색 알고리즘을 사용하는 대표적인 사례라고 볼 수 있죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이번 이야기에서는 가장 기본적인 방식의 Naïve 문자열 검색 알고리즘을 시작으로 보다 빠르고 효율적인 방식의 DFA와 KMP 알고리즘까지 차례대로 살펴보겠습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;문제 상황&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;문자열 $bananbanana$ 에서 $banana$를 찾아봅시다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;검색의 대상이자 주어진 문자열 ($bananbanana$)을 $T$ (Text) 그리고 찾고 싶은 문자 ($banana$)를 $P$ (Pattern)라 했을 때, 각 문자열의 특성을 다음과 같이 정의하겠습니다.&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$N$ : 텍스트 $T$의 길이&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$M$ : 패턴 $P$의 길이&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Sigma$ : 패턴 $P$에 등장하는 모든 문자(알파벳)의 집합&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;당연하게도 검색에 성공하기 위해선 패턴 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;background-color: #8cb3be;&quot;&gt;&lt;b&gt;$P$ 는&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;background-color: #8cb3be;&quot;&gt;&lt;b&gt;$T$ 의 부분 문자열&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이어야 하며, $T$의 길이인 $N$이 $P$ 의 길이 $M$ 보다 작을 경우 ($N &amp;lt; M$) 텍스트 내에 패턴이 포함될 수 없기 때문에 문자열 검색은 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;background-color: #8cb3be;&quot;&gt;&lt;b&gt;$N$이 $M$ 보다 크거나 같은 경우&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 고려합니다. (typically $N \gg M$)&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그럼 이제 패턴&amp;nbsp;$P$ $banana$&amp;nbsp;가 텍스트&amp;nbsp;$T$ $bananbanana$ 내에 등장하는지 찾아보겠습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCClvP/btsJd7hK5zL/jLeWoryapCBbu7VUNuKU90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCClvP/btsJd7hK5zL/jLeWoryapCBbu7VUNuKU90/img.png&quot; data-alt=&quot;정말 쉬운 문제죠&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCClvP/btsJd7hK5zL/jLeWoryapCBbu7VUNuKU90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCClvP%2FbtsJd7hK5zL%2FjLeWoryapCBbu7VUNuKU90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2046&quot; height=&quot;478&quot; data-origin-width=&quot;2046&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;정말 쉬운 문제죠&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;눈으로 봐도 풀 수 있는 정말 쉬운 문제입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;첫 글자부터 차례차례 비교해보면 주어진 문자열의&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;6번째 위치&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;에 있는 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;B&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 부터 끝까지 패턴&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;바나나 &lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;와 완벽하게 일치한다는 것을 알 수 있죠. 정리하자면 패턴 $P$는 텍스트 $T$ 내에 한 번 등장하며 그 위치는 6이라고 할 수 있습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그렇다면 컴퓨터는 이 문제를 어떻게 풀 수 있을까요?&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Naïve search algorithm&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;Naïve Search 알고리즘&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;은 말 그대로&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;순박한 알고리즘&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;입니다. &lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;반복문을&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;i&gt;Nested 하게&lt;/i&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;사용하면서 주먹구구식으로 가능한 모든 위치에서 패턴의 등장 여부를 판단하기 때문에&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;Brute-Force&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;알고리즘이라고도 할 수 있죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; naiveSearch(String text, String pattern) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;List&amp;lt;Integer&amp;gt; matched = new ArrayList&amp;lt;&amp;gt;();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int n = text.length();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int m = pattern.length();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i = 0; i &amp;lt;= n - m; i++) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int j = 0;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while (j &amp;lt; m &amp;amp;&amp;amp; text.charAt(i + j) == pattern.charAt(j)) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;j++;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (j == m) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;matched.add(i + 1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return matched;
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;코드에서 볼 수 있듯이 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;naiveSearch()&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;함수는 문자열 $T$&amp;nbsp;에 해당하는&amp;nbsp;($\sum_{i=0}^{N-M}$) iteration 과 패턴 $P$ 에 대한&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;($\sum_{j=0}^{M-1}$) iteration을 수행하면서 패턴의 등장 여부를 판단합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;698&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qqKoe/btsJhlGIs8g/c6mrkpEmskdlRR5FvdNqf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qqKoe/btsJhlGIs8g/c6mrkpEmskdlRR5FvdNqf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qqKoe/btsJhlGIs8g/c6mrkpEmskdlRR5FvdNqf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqqKoe%2FbtsJhlGIs8g%2Fc6mrkpEmskdlRR5FvdNqf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1524&quot; height=&quot;698&quot; data-origin-width=&quot;1524&quot; data-origin-height=&quot;698&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;예를 들어 $i=0, j=5$ 일 때,&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$T_{i+j}$와 $P_j$에 해당하는 문자가 일치하지 않기 때문에 $i=1$ iteration으로 넘어가게 됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;1504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfksfX/btsJiU17BuR/sNcSQA1Vn95IPiAgQAPs6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfksfX/btsJiU17BuR/sNcSQA1Vn95IPiAgQAPs6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfksfX/btsJiU17BuR/sNcSQA1Vn95IPiAgQAPs6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfksfX%2FbtsJiU17BuR%2FsNcSQA1Vn95IPiAgQAPs6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1486&quot; height=&quot;1504&quot; data-origin-width=&quot;1486&quot; data-origin-height=&quot;1504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이렇게 naiveSearch() 함수는 텍스트에 대해서 $\Theta (N)$, 패턴의 모든 문자가 일치할 때까지 $\Theta (M)$에 대한&amp;nbsp;iteration을 수행합니다. 텍스트의 모든 문자가 패턴과 동일할 경우는 흔치 않으므로 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;평균적으로 $\Theta (N + M)$&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;시간복잡도로 문제를 해결할 수 있는 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;올바른 알고리즘&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이라고 할 수 있죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;우리는 Naïve&amp;nbsp;할 수 없다&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;모든 상황에 대해서 $\Theta (N + M)$의 시간복잡도로 문제를 해결할 수 있다면 해당 알고리즘은 좋은 선택지가 될 것 입니다. 하지만 Naïve Search 알고리즘은 그렇지 못하죠.&lt;/span&gt;&lt;/p&gt;&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;&lt;div class=&quot;moreless-content&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;# 알고리즘에서 &quot;평균적으로&quot;라는 말이 의미하는 것은?&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;컴퓨터 공학, 특히 알고리즘에서&amp;nbsp;&lt;b&gt;&lt;i&gt;평균적으로&lt;/i&gt;&lt;/b&gt; 라는 말이 나온다는 것은 수행과정에서 무작위성의 절차 (&lt;a href=&quot;https://ko.wikipedia.org/wiki/%ED%99%95%EB%A5%A0%EC%A0%81_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;probabilistic or randomized&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;)가 존재하거나&amp;nbsp;&lt;u&gt;입력에 대해&amp;nbsp;민감하다&amp;nbsp;(sensitive)&lt;/u&gt;는 것을 의미합니다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;즉, 사용함에 있어 어느정도의 성능을 기대할 수 있지만 구조상 성능을 기대하기 힘든 Worst-case가 존재한다는 것이죠.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;저희가 자주 사용하는 &lt;b&gt;Hash형 자료구조&lt;/b&gt;가 가장 대표적인 사례입니다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;각 연산에 대해 평균적으로 $O(1)$을 기대할 수 있지만, 조회 및 삽입 과정에서 의도적인 &lt;b&gt;해시 충돌 (collision)&lt;/b&gt;을 일으켜 $O(N)$의 시간복잡도인 Worst-case가 발생하도록 유도할 수 있죠.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;실제로 백준, 코드포스와 같은 알고리즘 문제 풀이 서비스에서 종종 볼 수 있는 문제입니다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;해당 &lt;a href=&quot;https://codeforces.com/blog/entry/4898&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;포스트&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;를 읽어보신다면 어떻게 Hash 함수를 저격 (hack)한다는 것인지 알 수 있을 것입니다.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;어떤 상황이 문제가 될까요?&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;다음과 같은 문자열이 주어졌다고 해봅시다.&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$T$ : $aaaaaaaaaab$&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$P$ : $aaab$&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Sigma$ : $\{a, b\}$&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;1524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pK0JS/btsJgzsiRGG/CD1cYwB4KJ6w5uJvDH5BAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pK0JS/btsJgzsiRGG/CD1cYwB4KJ6w5uJvDH5BAk/img.png&quot; data-alt=&quot;backup 과 roll back이 반복하는 Worst-case&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pK0JS/btsJgzsiRGG/CD1cYwB4KJ6w5uJvDH5BAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpK0JS%2FbtsJgzsiRGG%2FCD1cYwB4KJ6w5uJvDH5BAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1672&quot; height=&quot;1524&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;1524&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;backup 과 roll back이 반복하는 Worst-case&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 상황에서 Naïve Search 알고리즘은 텍스트의&amp;nbsp;모든 문자열마다 패턴과 일치하는 지 판별하게 됩니다. 즉, $\Theta (N\cdot M)$의 시간복잡도로 해결할 수 밖에 없는 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;Worst-case&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;라는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그렇다면 이런 질문을 할 수 있겠죠.&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Naïve Search 알고리즘은 잘못된 알고리즘일까?&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;실제 환경에서 이러한 문제가 자주 발생할까?&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 알고리즘에 Worst-case가 존재한다는 것이 문제가 될까?&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;제 생각에 모든 질문의 답은 &quot;No&quot; 입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그렇다면 왜 우리는 naive 하지 말아야 할까요?&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Naïve Search 알고리즘은 올바른 답을 도출하는 알고리즘 입니다. 하지만 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;개선점이 존재&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;한다는 것이죠. 실제 환경에서 이러한 문제가 발생하지 않도록 통재할 수 없다면 우연의 일치로, 혹은 취약점을 알고 있는 악성 사용자로 인해 문제가 발생할 수 있겠죠. 게다가&amp;nbsp;Naïve Search 알고리즘의 Worst-case는 다른 알고리즘의 Worst-case 와는 비교도 할 수 없이 빈번하게 발생할 수 있는 문제기 때문에 개선이 필요한 상황입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;마지막으로 우리가 naïve 할 수 없는 가장 핵심적인 이유는 모든 상황에서 $\Theta (N)$의 시간복잡도로 문제를 해결할 수 있는 최적의 알고리즘을 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;누군가가 이미 만들어놨다는 것&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이죠. 그것도 상당히 적은 비용으로요!&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;DFA (Deterministic Finite State Automaton)&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;앞선 Naïve Search 알고리즘에서의 개선점은 선형 시간 (Linear time)으로 문제를 해결할 수 있도록 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;backup 과정을 생략&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;하는 것입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2052&quot; data-origin-height=&quot;964&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mMK1i/btsJhiJ8cMU/ndbEendZKj2dGWgGf0NPY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mMK1i/btsJhiJ8cMU/ndbEendZKj2dGWgGf0NPY0/img.png&quot; data-alt=&quot;aaa가 있다는 것을 알고 있기 때문에 굳이 j=0부터 볼 필요가 없겠죠&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mMK1i/btsJhiJ8cMU/ndbEendZKj2dGWgGf0NPY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmMK1i%2FbtsJhiJ8cMU%2FndbEendZKj2dGWgGf0NPY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2052&quot; height=&quot;964&quot; data-origin-width=&quot;2052&quot; data-origin-height=&quot;964&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;aaa가 있다는 것을 알고 있기 때문에 굳이 j=0부터 볼 필요가 없겠죠&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$i=0$ 일 때를 생각해봅시다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$j=0$부터 $j=3$ 까지 비교하면서 $j=2$일 때까지 모든 문자일이 &quot;일치&quot;했습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;다음으로 $j=3$에서 텍스트와 패턴이 일치하지 않았죠. ($T_{i+j} \neq P_{j}$)&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;전체적인 매칭은 실패지만 현재까지 확인한 텍스트가 $aaaa$라는 중요한 사실을 알 수 있었습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그렇다면 다음 과정은 무엇일까요?&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$i=1, j=0$부터 $j=2$까지 이미 탐색한 문자열을 제외하고 (avoid backup) $i=1, j=3$부터 문자열을 비교하면 됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그렇게 $i=7, j=3$까지 비교하면서 정확히 11번 ($N$)의 비교만으로 $T$에 대한 모든 $P$의 등장 위치를 반별할 수 있게 되는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;어떻게 이런 결과가 나올 수 있는 것일까요?&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;정답은 $T$를 이루는 모든 문자열($\Sigma$)이 각각의 비교하는 문자에 대한 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;유한한 상태&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 갖기 때문입니다. (Exactly on &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;transition&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; for each char)&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;다시 말해, &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;&quot;지금 비교하는 텍스트의 알파벳이 무엇이냐&quot;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;에 따라 다음에 비교할 알파벳이 정해진다는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2504&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ejQXDX/btsJgHcHltb/Wj9YzDbALcRjmO6F7hGkGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ejQXDX/btsJgHcHltb/Wj9YzDbALcRjmO6F7hGkGk/img.png&quot; data-alt=&quot;State 0&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ejQXDX/btsJgHcHltb/Wj9YzDbALcRjmO6F7hGkGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FejQXDX%2FbtsJgHcHltb%2FWj9YzDbALcRjmO6F7hGkGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2504&quot; height=&quot;694&quot; data-origin-width=&quot;2504&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;State 0&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;텍스트에서 패턴 $aaab$의 첫 번째 문자 ($j=0$)를 비교해봅시다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;저희는 $a$를 제외한 그 어떤 문자도 필요하지 않죠. (첫 글자가 다른데 같은 단어일 수 없으니까요!)&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;만약, 지금 비교하는 문자가 $a$가 아니라면 ($T_{i+0} \neq P_0$) 텍스트의 다음 문자 ($T_{i+1}$)가 $a$인지 확인해보면 됩니다. 그리고 지금 확인한 문자가 $a$라면 ($T_{i+0} = P_0$) 첫 번째 문자가 일치한다는 것이니 두 번째 문자 (j=1)로 넘어가면 됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2476&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5H91R/btsJhMc39aT/9buBJgnH1TkBxucxwMgh31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5H91R/btsJhMc39aT/9buBJgnH1TkBxucxwMgh31/img.png&quot; data-alt=&quot;State 1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5H91R/btsJhMc39aT/9buBJgnH1TkBxucxwMgh31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5H91R%2FbtsJhMc39aT%2F9buBJgnH1TkBxucxwMgh31%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2476&quot; height=&quot;728&quot; data-origin-width=&quot;2476&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;State 1&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;다음으로 패턴 $aaab$의 두 번째 문자 ($j=1$)를 비교해봅시다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이전과 마찬가지로 저희에겐 지금 비교하는 문자 ($T_{i+1}$)가 $a$냐 아니냐만이 중요합니다. 비교하는 문자가 $a$라면 다음으로 넘어가 세 번째 문자와 비교를, 그렇지 않다면 다시 첫 번째 문자부터 비교하면 되기 때문입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2434&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDya2r/btsJh9FGECD/uadqoUxKnjlIEbB0uLxhn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDya2r/btsJh9FGECD/uadqoUxKnjlIEbB0uLxhn0/img.png&quot; data-alt=&quot;State3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDya2r/btsJh9FGECD/uadqoUxKnjlIEbB0uLxhn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDya2r%2FbtsJh9FGECD%2FuadqoUxKnjlIEbB0uLxhn0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2434&quot; height=&quot;794&quot; data-origin-width=&quot;2434&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;State3&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;세 번째 문자를 비교하는 것도 이전과 동일하기 때문에 바로 네 번째 문자를 비교해보겠습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;네 번째 문자부터는 조금 달라지게 됩니다. 비교하는 문자와 패턴이 일치하는 문자가 바로 $b$기 때문이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;우선, 지금 비교하는 문자가 $a$와 $b$가 아니라면 ($T_{i+j} \notin \Sigma$) 저희는 다음부터 비교할 문자를 패턴의 첫 문자부터 비교하면 됩니다. 패턴에 포함되지 않는 문자가 존재하면 패턴과 일치할 수 없기 때문입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;다음으로 $a$일 때는 어떻게 될까요? 현재까지 확인한 텍스트가 $aaaa$라는 뜻이므로 다음에 비교할 텍스트 문자를 다시 패턴의 네 번째 문자와 비교하면 됩니다. 패턴의 첫 번째 문자부터 세 번째 문자까지 $aaa$를 만족하는 것은 동일하므로 처음부터 다시 비교할 필요가 없다는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그렇다면 비교하는 문자가 $b$라면 어떻게 될까요? 저희가 원하는 결과죠! 텍스트에서&amp;nbsp;지금까지 비교한 문자열이 패턴과 정확히 일치한다는 것이니 패턴의 등장 위치 ($i$)를 기억하고 계속해서 다음 문자를 비교하면 됩니다.&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clz1VF/btsJh7ulGEa/1hgdes23TWgNE5adKE6Jd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clz1VF/btsJh7ulGEa/1hgdes23TWgNE5adKE6Jd1/img.png&quot; data-alt=&quot;Deterministic Finite State Automaton&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clz1VF/btsJh7ulGEa/1hgdes23TWgNE5adKE6Jd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fclz1VF%2FbtsJh7ulGEa%2F1hgdes23TWgNE5adKE6Jd1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2556&quot; height=&quot;858&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Deterministic Finite State Automaton&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;컴퓨터 공학에서 이러한 개념을&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Deterministic_finite_automaton&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;DFA&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; (Deterministic finite automaton)라고 정의합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;각각의 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;state&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 마다 입력에 따른&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;유일한 상태 변화&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;가 존재하기 때문에 다음과 같은 다이어그램으로 표현할 수 있다는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이제 실제 코드로 DFA searching을 확인해보겠습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; dfaSearch(String text, String pattern) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int n = text.length();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int m = pattern.length();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;List&amp;lt;Integer&amp;gt; matched = new ArrayList&amp;lt;&amp;gt;();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int state = 0;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i = 0; i &amp;lt; n; i++) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;state = dfa[state][text.charAt(i)];

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (state == m) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;matched.add(i - m + 2);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;state = dfa[state][pattern.charAt(m - 1)];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return matched;
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;dfaSearch()&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 함수는 state 변수를 통해 현재 상태를 관리합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;주어진 텍스트에 대한 iteration을 돌면서 현재 state를 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;사전에 계산한 dfa[][] 배열&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;을 통해 비교하는 텍스트의 문자에 해당하는 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;state로 변경&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;합니다. 변경을 하면서 state가 패턴과 완전히 일치하게 되면 (Accept state) 패턴의 등장 위치를 저장한 후 패턴의 마지막 문자에 해당하는 state로 이동하게 됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;DFA를 활용한 문자열 검색 알고리즘 $\Theta (N)$의 시간복잡도로 텍스트에 등장하는 모든 패턴의 위치를 찾을 수 있는 빠르고 개선된 알고리즘 입니다. 결국, $\Sigma$에 해당하는 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;state 상태 변화를 사전에 계산&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;한 후 이를 활용하는 &lt;/span&gt;&lt;a href=&quot;https://codingmon.dev/2&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;메모이제이션&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;의 응용이라고 볼 수 있는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;DFA를 활용해 $\Theta (N)$의 시간복잡도로 문자열 검색 문제를 해결할 수 있다는 것은 매우 큰 강점이지만 한 가지 단점이 존재하죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;문자열을 비교하기 전에 state 상태 변화를 계산하는 과정을 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;전처리&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;preprocessing&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;)라고 부릅니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;DFA searching 알고리즘은 전처리 과정에서 패턴에 등장하는 모든 문자($\Sigma$)에 대해 계산하기 위해 $O(\vert\Sigma\vert\cdot M)$의 시간복잡도가 필요하며 state 정보를 저장하기 위한 $O(\vert\Sigma\vert\cdot M)$의 공간복잡도가 필요합니다.&lt;/span&gt;&lt;/p&gt;&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;&lt;div class=&quot;moreless-content&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #fafafa; font-family: Noto Serif KR;&quot;&gt;# DFA preprocessing 함수 살펴보기 (&lt;span style=&quot;color: #333333; background-color: #fafafa; font-family: Noto Serif KR;&quot;&gt;Extended Ascii&lt;/span&gt;)&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;int[][] buildDFA(String pattern) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int m = pattern.length();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int[][] dfa = new int[m + 1][256]; // ascii range

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dfa[0][pattern.charAt(0)] = 1;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int x = 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int j = 1; j &amp;lt;= m; j++) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.arraycopy(dfa[x], 0, dfa[j], 0, 256);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (j &amp;lt; m) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dfa[j][pattern.charAt(j)] = j + 1;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;x = dfa[x][pattern.charAt(j)];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Transition for accept state
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dfa[m][pattern.charAt(m - 1)] = x;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return dfa;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;확장된 ascii 문자의 범위가 0부터 255라는 것을 생각하면 $\Sigma$는 256에 정도지만 범용적으로 사용하는 유니코드는 $2^{32}$ 정도로 저장하는 것은 불가능하며, 패턴의 길이 ($M$)가 10만 정도를 넘어간다면 전처리 과정에서 소요하는 시간과 배열의 크기가 상당히 커지기 때문에 일반적인 상황에서 가볍게 사용하기엔 적합하지 않다는 것입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;다시 말해 DFA string search 알고리즘에도&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;개선점이 존재&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;한다는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;KMP (Knuth-Morris-Pratt) algorithm&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;DFA string search 알고리즘의 문제는 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;전처리 과정&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;에서 많은 시간이 소모 된다는 것 그리고 $\Sigma$에 해당하는 state 정보를 저장하기 위해 많은 공간이 필요하다는 것입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2522&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1YGQc/btsJir0mMXe/14AgzHuPgoSgsujCj2VUDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1YGQc/btsJir0mMXe/14AgzHuPgoSgsujCj2VUDK/img.png&quot; data-alt=&quot;DFA&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1YGQc/btsJir0mMXe/14AgzHuPgoSgsujCj2VUDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1YGQc%2FbtsJir0mMXe%2F14AgzHuPgoSgsujCj2VUDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2522&quot; height=&quot;834&quot; data-origin-width=&quot;2522&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DFA&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이전 DFA 다이어그램을 보면 각 state 마다 비교하는 문자에 대한 모든 경우의 수 ($\Sigma$)를 정의했습니다. 그렇기 때문에 패턴에 있는 모든 문자마다 $\Sigma$에 해당하는 2차원 배열이 필요했던 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p6lFU/btsJgP9pHWg/vxrBsjKocqz9fX45fOApA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p6lFU/btsJgP9pHWg/vxrBsjKocqz9fX45fOApA0/img.png&quot; data-alt=&quot;이렇게 표현해볼까요?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p6lFU/btsJgP9pHWg/vxrBsjKocqz9fX45fOApA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp6lFU%2FbtsJgP9pHWg%2FvxrBsjKocqz9fX45fOApA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2574&quot; height=&quot;858&quot; data-origin-width=&quot;2574&quot; data-origin-height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이렇게 표현해볼까요?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;각 state 마다&amp;nbsp;$\Sigma$에 해당하는 모든 경우의 수를 계산하는 것이 아닌&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;맞다, 틀리다에 대해서만 저장&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;한다면 어떨까요? 위 다이어그램은 각 state에서 비교하는 텍스트의 문자와 패턴이 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;일치할 때 (1)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;와 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;불일치할&amp;nbsp;때 (0)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;에 대해서만 표현한 결과입니다. 그렇다면 이걸 배열로서 표현하면 어떨까요?&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1092&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7WO3d/btsJiHaWcUD/JBffKbesn5Ul8wZP7onUOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7WO3d/btsJiHaWcUD/JBffKbesn5Ul8wZP7onUOK/img.png&quot; data-alt=&quot;1차원 배열로도 충분히 표현이 가능하네요!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7WO3d/btsJiHaWcUD/JBffKbesn5Ul8wZP7onUOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7WO3d%2FbtsJiHaWcUD%2FJBffKbesn5Ul8wZP7onUOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1814&quot; height=&quot;1092&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1092&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1차원 배열로도 충분히 표현이 가능하네요!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;저희는 각 state에서 텍스트와 패턴을 비교해오면서 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;현재 state까지 모든 문자열이 일치한다&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;는 사실을 알고 있습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이러한 사실을 바탕으로 각 state에서 매칭에 실패했을 때 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;패턴의 몇 번째 문자부터 다시 비교&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 하면 되는지 알 수 있는 것이죠&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;&lt;div class=&quot;moreless-content&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; background-color: #fafafa; font-family: Noto Serif KR;&quot;&gt;# KMP는 논문의 제목이 아니다! (&lt;i&gt;A linear pattern-matching algorithm&lt;/i&gt;)&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;KMP 알고리즘은 이름에서 유추할 수 있듯이 Knuth, Morris, Pratt 세 명의 학자의 연구 결과로 1970년&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;A linear pattern-matching algorithm&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이라는 Technical Report (&lt;a href=&quot;https://cpsc.yale.edu/sites/default/files/files/technical-reports/TR17%20Linear%20Pattern%20Matching%20ALgorithms.pdf&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;TR-40&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;)로 발표됐습니다.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;KMP 알고리즘은 실패함수 ($pi$)를 사용해 불필요한 문자열 간의 비교를 생략하는 방식을 사용합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;본문에서 사용하는 fail[] 배열이 실패함수 ($pi$)로서의 기능을 한다고 생각해주시면 됩니다.&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;List&amp;lt;Integer&amp;gt; match(String text) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;List&amp;lt;Integer&amp;gt; matched = new ArrayList&amp;lt;&amp;gt;();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char[] t = text.toCharArray();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int j = 0;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i = 0; i &amp;lt; t.length; i++) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while (j &amp;gt; 0 &amp;amp;&amp;amp; t[i] != p[j]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;j = fails[j - 1];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (t[i] != p[j]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (j == p.length - 1) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;matched.add(i - j + 1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;j = fails[j];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} else {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;j++;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return matched;
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;앞선 예제인 텍스트 $aaaaaaaaaab$, 패턴 $aaab$에 대해 KMP 알고리즘이 어떻게 동작하는지 알아보겠습니다&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;1186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYQNBB/btsJh9y15jj/R59DfRU8nUZrFR4PAYMIgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYQNBB/btsJh9y15jj/R59DfRU8nUZrFR4PAYMIgK/img.png&quot; data-alt=&quot;문자가 일치하지 않을 경우 fail[] 배열을 활용한다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYQNBB/btsJh9y15jj/R59DfRU8nUZrFR4PAYMIgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYQNBB%2FbtsJh9y15jj%2FR59DfRU8nUZrFR4PAYMIgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1874&quot; height=&quot;1186&quot; data-origin-width=&quot;1874&quot; data-origin-height=&quot;1186&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문자가 일치하지 않을 경우 fail[] 배열을 활용한다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$i=0, j=3$ 일 때, 처음으로 텍스트와 패턴의 문자가 불일치하게 됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;바로 그때 함수 내에 있는 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;while iteration&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;을 거치게 되죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;while 문에서 비교할 패턴의 문자에 해당하는 인덱스 $j$가 텍스트와 일치할 때까지 $j := fail[j - 1]$로 재할당됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc6OcM/btsJia5GfQB/T1zxPfSzGd3v6MXfnlIP4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc6OcM/btsJia5GfQB/T1zxPfSzGd3v6MXfnlIP4k/img.png&quot; data-alt=&quot;fail[] 배열을 통해 불일치할 때까지 재할당하게 됩니다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc6OcM/btsJia5GfQB/T1zxPfSzGd3v6MXfnlIP4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc6OcM%2FbtsJia5GfQB%2FT1zxPfSzGd3v6MXfnlIP4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1814&quot; height=&quot;1052&quot; data-origin-width=&quot;1814&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;fail[] 배열을 통해 불일치할 때까지 재할당하게 됩니다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;인덱스 $j$가 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;fail[] 배열을 통해 2로 재할당&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 되지만 기존에 알고 있는 텍스트 $aaaa$가 패턴 $aaa$와 일치하기 때문에 $i=1, j=3$로 다시 비교를 시작하게 됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0zNlY/btsJhZ4pS0v/ujJQunRkUj0dtP67OcQe0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0zNlY/btsJhZ4pS0v/ujJQunRkUj0dtP67OcQe0k/img.png&quot; data-alt=&quot;마지막에 match가 되면서 j는 fail[j]로 변경되죠&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0zNlY/btsJhZ4pS0v/ujJQunRkUj0dtP67OcQe0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0zNlY%2FbtsJhZ4pS0v%2FujJQunRkUj0dtP67OcQe0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1670&quot; height=&quot;916&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;마지막에 match가 되면서 j는 fail[j]로 변경되죠&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;반복문을 거치고 거쳐 마지막 $i=7, j=4$까지 모든 패턴이 일치하는 상황이 됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이때 모든 반복문은 종료되게 되지만, &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;$j$는 fail[j - 1]이 아닌 &lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;&lt;b&gt;fail[j]&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;로 할당이 되면서 반복문이 끝나지 않은 상황에서 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;패턴의 마지막 문자가 일치했을 때의 &lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;&lt;b&gt;state&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; (Transition for accept state)로 변경이 되는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;결국, KMP 알고리즘의 핵심은 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;패턴에 대한 실패함수를 계산하는 것&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;입니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이전 DFA 전처리 과정에서 $O(\vert\Sigma\vert\cdot M)$의 시간복잡도가 소요되었지만 KMP 알고리즘의 전처리 과정은 $\Theta (M)$의 시간복잡도를 필요로 합니다. 여기서 fail[] 배열을 계산하는 전처리 과정은 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;KMP 알고리즘 그 자체와 상당히 유사한 구조&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;로 진행됩니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;int[] getFail(String pattern) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;char[] p = pattern.toCharArray();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int[] fail = new int[p.length];

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int j = 0;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i = 1; i &amp;lt; p.length; i++) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while (j &amp;gt; 0 &amp;amp;&amp;amp; p[i] != p[j]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;j = fail[j - 1];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (p[i] == p[j]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;j++;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;fail[i] = j;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return fail;
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;fail[] 배열을 계산하는 과정은 패턴과 패턴 그 자체를 비교하는 과정이라고 볼 수 있습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;단, 동일한 두 패턴을 비교하는 것이 아닌 $\sum\limits_{i=1}^{M-1} P_i$와 $\sum\limits_{j=0}^{M-1} P_j$를 비교하면서 $P_i$에 대해 $j$를 fail[] 배열을 통해 변경하고 각 $state_i$에 대한 fail[i] 값을 계산하는 것이죠.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이렇게 DFA를 활용한 알고리즘에서 KMP 알고리즘을 사용하는 것을 통해 $\Theta (M)$의 시간복잡도와 공간복잡도로 전처리 과정을 단축할 수 있었고 저희가 궁극적으로 원했던 모든 텍스트와 패턴에 대해 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;다항 시간&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; $\Theta (N)$으로 문제를 해결할 수 있는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;최적화된 알고리즘&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;을 구현할 수 있었습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이야기를 마치며&lt;/span&gt;&lt;/h2&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이번 이야기에서 간단한 방식의&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;&lt;b&gt;Naïve&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 문자열 검색 알고리즘에서부터 Worst-case에 대한 소요 시간을 개선하기 위해&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;state&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 활용하는 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;&lt;b&gt;DFA&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;, 그리고 전처리 과정의 소요 시간 및 공간적인 제약을 개선한 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;&lt;b&gt;KMP&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 알고리즘까지 상당히 많은 볼륨을 다뤘습니다. 그 과정에서 메모이제이션에 대한 내용과 Hash형 자료구조에 대한 내용도 잠깐 언급되면서 다양한 컴퓨터 공학의 개념을 다룰 수 있어서 뿌듯한 감정도 드는 것 같습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;여담으로 문자열 검색은 이번에 살펴본 것 이상으로 다양한 알고리즘이 존재합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 105px;&quot; border=&quot;1&quot; data-ke-style=&quot;style1&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 38.3333%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Alogorithm&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.2868%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Preprocessing time&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.7132%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Average time&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.6667%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Worst time&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 38.3333%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Naïve&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.2868%; height: 21px; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;0&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.7132%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (n+m)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.6667%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (n\cdot m)$&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 38.3333%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Rabin-Karp&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.2868%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (m)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.7132%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (n)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.6667%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$O(n\cdot m)$&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 38.3333%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Deterministic Finite State Automaton&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.2868%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$O(m \cdot\vert\Sigma\vert)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.7132%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (n)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.6667%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (n)$&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 21px;&quot;&gt;&lt;td style=&quot;width: 38.3333%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Knuth-Morris-Pratt&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.2868%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (m)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.7132%; height: 21px;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (n)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.6667%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (n)$&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 38.3333%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Two-way matching&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.2868%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\Theta (m)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 22.7132%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$O(n)$&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;width: 16.6667%;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;$\lceil O(\log m)\rceil$&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;궁금해하실 분들을 위해 간략하게 소개드리자면 Hashing을 사용하는 &lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Rabin-Karp&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 알고리즘과 Java의 String.indexOf()에서 사용하는 &lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Two-way_string-matching_algorithm&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;Two-way matching&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 알고리즘도 중요한 문자열 검색 알고리즘 입니다. 각각의 알고리즘의 내부 구현 방식은 차이가 있지만 그 중에서도 가장 핵심이 되는 KMP 알고리즘을 이해한 여러분이라면 다른 알고리즘들도 큰 무리 없이 이해할 수 있을 것이라 생각합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;끝으로 이론적인 내용을 넘어서 직접 활용해보고 싶은 분도 계실 것 같은데요.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그런 분들께 &lt;/span&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1786&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;백준 1786번 찾기 문제&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 추천드립니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;직접 Naïve, DFA, KMP를 구현해보면서 시간 초과, 메모리 초과, 성공으로 나아가는 과정을 느껴보신다면 더욱 깊이 있는 이해도를 갖추실거라 장담합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;여기까지가 문자열 검색 (String searching) 이야기였습니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 글이 저를 포함한 모든 독자분들께 개발자로 성장하는 여정의 밑거름이자 나침반으로써 도움이 되었기를 바랍니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;감사합니다.&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>dfa 알고리즘</category>
      <category>KMP 알고리즘</category>
      <category>naive 알고리즘</category>
      <category>string matching</category>
      <category>string searching</category>
      <category>문자열 검색</category>
      <category>문자열 매칭</category>
      <category>문자열 알고리즘</category>
      <category>문자열 탐색</category>
      <category>백준 1786</category>
      <author>코딩몬</author>
      <guid isPermaLink="true">https://kazy.tistory.com/3</guid>
      <comments>https://kazy.tistory.com/3#entry3comment</comments>
      <pubDate>Fri, 30 Aug 2024 01:00:47 +0900</pubDate>
    </item>
    <item>
      <title>메모이제이션 (Memoization) 이야기</title>
      <link>https://kazy.tistory.com/2</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이야기에 앞서&lt;/span&gt;&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이번 이야기는 &lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #5c5cb2;&quot;&gt;&lt;b&gt;메모이제이션 (Memoization)&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #5c5cb2;&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;입니다.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;정확히 같은 뜻은 아니지만 편의상 더 넓은 개념인&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%8F%99%EC%A0%81_%EA%B3%84%ED%9A%8D%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #00aba6;&quot;&gt;&lt;b&gt;동적계획법 &lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%8F%99%EC%A0%81_%EA%B3%84%ED%9A%8D%EB%B2%95&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #00aba6;&quot;&gt;&lt;b&gt;(Dynamic Programming)&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #00aba6;&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이라고도 부르죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;동일한 문제를 반복적으로 계산해야 할 때&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이전에 계산한 결과를 기억해 중복적인 계산 과정을 생략한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;메모이제이션은 문제를 해결할 때&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;&quot;이미 계산한 결과가 있으면 다시 계산하지 않는다&quot;&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;라는 간단한 개념에서부터 시작합니다. 정말 단순한 내용이지만 정확히 어떤 상황에서 사용할 수 있는 개념인지 그리고 어떤 방법으로 계산된 결과를 저장할 수 있는지는 말로만 듣고 이해하기란 쉬운 일이 아니죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그래서 이번 이야기에서는&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1520&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #00aba6;&quot;&gt;백준 1520번&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; 문제 내리막 길을 직접 풀어보면서 메모이제이션에 대한 전반적인 개념을 이해하고 코드로서 표현해보도록 하겠습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;img&amp;quot;,&amp;quot;align&amp;quot;:&amp;quot;inner-center&amp;quot;,&amp;quot;mobileAlign&amp;quot;:&amp;quot;content-full&amp;quot;,&amp;quot;url&amp;quot;:&amp;quot;http://t1.daumcdn.net/brunch/service/user/dQUf/image/b7AzYOgIKBKSL_mL1lsN5dKVlGU.jpeg&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;내리막 길을 이용해 목적지로 갈 수 있는 경우의 수를 효율적으로 구해보자&amp;quot;,&amp;quot;width&amp;quot;:&amp;quot;500&amp;quot;,&amp;quot;height&amp;quot;:&amp;quot;500&amp;quot;,&amp;quot;originalName&amp;quot;:&amp;quot;&amp;quot;}&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eCKNJc/btsIUKFNtyW/9shynDZ4WlKG5VbJsdgaC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eCKNJc/btsIUKFNtyW/9shynDZ4WlKG5VbJsdgaC0/img.png&quot; data-alt=&quot;내리막 길을 이용해 목적지로 갈 수 있는 경우의 수를 효율적으로 구해보자&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eCKNJc/btsIUKFNtyW/9shynDZ4WlKG5VbJsdgaC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeCKNJc%2FbtsIUKFNtyW%2F9shynDZ4WlKG5VbJsdgaC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;500&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내리막 길을 이용해 목적지로 갈 수 있는 경우의 수를 효율적으로 구해보자&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;문제 상황&lt;/span&gt;&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;여행을 떠난 세준이는 지도를 하나 구하였다. 이 지도는 아래 그림과 같이 직사각형 모양이며 여러 칸으로 나뉘어져 있다. 한 칸은 한 지점을 나타내는데 각 칸에는 그 지점의 높이가 쓰여 있으며, 각 지점 사이의 이동은 지도에서 상하좌우 이웃한 곳끼리만 가능하다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;...중략...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;지도가 주어질 때 이와 같이 제일 왼쪽 위 지점에서 출발하여 제일 오른쪽 아래 지점까지 항상 내리막길로만 이동하는 경로의 개수를 구하는 프로그램을 작성하시오.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;- 백준 1520번 내리막길 중 -&lt;/span&gt;&lt;/blockquote&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;img&amp;quot;,&amp;quot;align&amp;quot;:&amp;quot;content-left&amp;quot;,&amp;quot;mobileAlign&amp;quot;:&amp;quot;content-full&amp;quot;,&amp;quot;url&amp;quot;:&amp;quot;http://t1.daumcdn.net/brunch/service/user/dQUf/image/Lm874It3I-mYwM6GaenCI9gY98c&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;width&amp;quot;:&amp;quot;302&amp;quot;,&amp;quot;height&amp;quot;:&amp;quot;244&amp;quot;,&amp;quot;originalName&amp;quot;:&amp;quot;&amp;quot;}&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;244&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pq4cH/btsIUJNEWHh/T5zPK2RddjuvHLsIlFUHj1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pq4cH/btsIUJNEWHh/T5zPK2RddjuvHLsIlFUHj1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pq4cH/btsIUJNEWHh/T5zPK2RddjuvHLsIlFUHj1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpq4cH%2FbtsIUJNEWHh%2FT5zPK2RddjuvHLsIlFUHj1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;302&quot; height=&quot;244&quot; data-origin-width=&quot;302&quot; data-origin-height=&quot;244&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;세준이가 지도를 갖고 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;지도의 좌측 상단 (1, 1)에서 출발해서 우측 하단 (4, 5)로 이동할 수 있는&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt; 모든 경로의 수&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 알고 싶다고 하군요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이때 상하좌우 어디로든 갈 수 있지만 현재 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;타일에 적혀있는 숫자보다 작은 타일로만 이동&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;할 수 있다고 합니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;img&amp;quot;,&amp;quot;align&amp;quot;:&amp;quot;content-left&amp;quot;,&amp;quot;mobileAlign&amp;quot;:&amp;quot;full&amp;quot;,&amp;quot;url&amp;quot;:&amp;quot;http://t1.daumcdn.net/brunch/service/user/dQUf/image/H_VxT3Kf6oFFglmQ5EvNO08Yvjw.png&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;width&amp;quot;:&amp;quot;841&amp;quot;,&amp;quot;height&amp;quot;:&amp;quot;660&amp;quot;,&amp;quot;originalName&amp;quot;:&amp;quot;&amp;quot;}&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lRYE3/btsITT4ydpN/skstqDbl9eyI0tMIaTv3Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lRYE3/btsITT4ydpN/skstqDbl9eyI0tMIaTv3Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lRYE3/btsITT4ydpN/skstqDbl9eyI0tMIaTv3Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlRYE3%2FbtsITT4ydpN%2FskstqDbl9eyI0tMIaTv3Tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;841&quot; height=&quot;660&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;해당 지도에서는 총 3가지 경로가 존재하네요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;빨간색, 파란색, 초록색 경로를 따라 이동한다면 상하좌우&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그리고 내리막 길로 이동한다는 규칙을 만족하면서 목적지로 도착할 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그렇다면 이렇게 모든 경로의 수를 구하기 위해선 어떻게 해야 할까요?&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이렇게 표현해 볼까요?&lt;/span&gt;&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;각각의 경로가 타일을 하나하나 거쳐온 길을 보면 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;특정 부분&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;분기점&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;에서 갈라지고&amp;nbsp;다시 모이게 되는데요.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;경로가 그려진 그림을 보면 뭔가 새로운 정보를 도출해 낼 수 있을 것 같습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;한 번 각 경로가 지나오는 경로에 숫자를 표기해 보겠습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;img&amp;quot;,&amp;quot;align&amp;quot;:&amp;quot;inner-center&amp;quot;,&amp;quot;mobileAlign&amp;quot;:&amp;quot;content-full&amp;quot;,&amp;quot;url&amp;quot;:&amp;quot;http://t1.daumcdn.net/brunch/service/user/dQUf/image/Wbt6OZB7TQrZT7mK6bYxAUrQ4i8.png&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;이제 규칙이 보이는군요&amp;quot;,&amp;quot;width&amp;quot;:&amp;quot;500&amp;quot;,&amp;quot;height&amp;quot;:&amp;quot;402&amp;quot;,&amp;quot;originalName&amp;quot;:&amp;quot;&amp;quot;}&quot;&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cA7Apk/btsISfgFp7K/CxpLTMyEqXHG0kEQty6nUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cA7Apk/btsISfgFp7K/CxpLTMyEqXHG0kEQty6nUK/img.png&quot; data-alt=&quot;이제 규칙이 보이는군요&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cA7Apk/btsISfgFp7K/CxpLTMyEqXHG0kEQty6nUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcA7Apk%2FbtsISfgFp7K%2FCxpLTMyEqXHG0kEQty6nUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;402&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이제 규칙이 보이는군요&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;각 경로의 분기점에서 해답에 대한 실마리가 보이기 시작합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;결국&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;목적지로 갈 수 있는 모든 경로의 수&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #16b06d;&quot;&gt;&lt;b&gt;목적지 타일로 갈 수 있는 타일&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;들의 경로의 합과 같습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그런데 이게 왜 메모이제이션이라는 걸까요? 그냥 평범한 그래프 탐색 문제로 보이지 않나요?&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;왜 메모이제이션인가?&lt;/span&gt;&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;규칙을 찾으면서 이게 왜 메모이제이션일까? 이해하지 못하는 분도 계실 것 같습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그 이유는 단순합니다. 저희가 아직 문제를 해결하기 위해 어떠한 접근도 하지 않았기 때문이죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;문제를 풀기 위해 이렇게 접근해 보겠습니다. 다음과 같은 함수가 있다고 생각해보죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;img&amp;quot;,&amp;quot;align&amp;quot;:&amp;quot;content-full&amp;quot;,&amp;quot;mobileAlign&amp;quot;:&amp;quot;full&amp;quot;,&amp;quot;url&amp;quot;:&amp;quot;http://t1.daumcdn.net/brunch/service/user/dQUf/image/VC2wbnc3Hsj650iq6KCyLQykDPE.png&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;width&amp;quot;:&amp;quot;1204&amp;quot;,&amp;quot;height&amp;quot;:&amp;quot;1496&amp;quot;,&amp;quot;originalName&amp;quot;:&amp;quot;&amp;quot;}&quot;&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;pre class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Main {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 중략
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public static void main(String[] args) throws IOException {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init(); // padding을 통해 (1, 1) -&amp;gt; (n, m)에 도착하도록 구성

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(travel(n, m));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private static int travel(int r, int c) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (r == 1 &amp;amp;&amp;amp; c == 1) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 1;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int path = 0;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (map[r][c] &amp;lt; map[r + 1][c]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;path += travel(r + 1, c);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (map[r][c] &amp;lt; map[r - 1][c]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;path += travel(r - 1, c);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (map[r][c] &amp;lt; map[r][c + 1]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;path += travel(r, c + 1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (map[r][c] &amp;lt; map[r][c - 1]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;path += travel(r, c - 1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return path;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;도착지 (n, m)에서 출발해서 인접한 타일 중 올라갈 수 있는 다음 타일로 이동한 후&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;재귀적으로&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;다음 타일의 값을 호출합니다. 올라갈 수 있는 타일로 이동하면&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;탈출 조건&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;인 출발지 (1, 1)에서 1을 반환하기 때문에 결과적으로 출발지에서 도착지까지 갈 수 있는 모든 경로의 수를 반환하게 됩니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;올바른 답을 도출할 수 있는 방법이네요! 하지만&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;효율적&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이라고 할 수 있을까요?&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;div data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;img&amp;quot;,&amp;quot;align&amp;quot;:&amp;quot;content-left&amp;quot;,&amp;quot;mobileAlign&amp;quot;:&amp;quot;full&amp;quot;,&amp;quot;url&amp;quot;:&amp;quot;http://t1.daumcdn.net/brunch/service/user/dQUf/image/RTD9sscphznk8X5Cu9exVZp0pX0.png&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;width&amp;quot;:&amp;quot;831&amp;quot;,&amp;quot;height&amp;quot;:&amp;quot;676&amp;quot;,&amp;quot;originalName&amp;quot;:&amp;quot;&amp;quot;}&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;676&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0xHMT/btsIUsyEj93/tCxG50mEbgkip6GMSkZIs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0xHMT/btsIUsyEj93/tCxG50mEbgkip6GMSkZIs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0xHMT/btsIUsyEj93/tCxG50mEbgkip6GMSkZIs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0xHMT%2FbtsIUsyEj93%2FtCxG50mEbgkip6GMSkZIs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;831&quot; height=&quot;676&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;676&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그림을 보면 파란색 경로 찾기 위해&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;&amp;nbsp;32번 타일 (1, 4)의 값을 미리 계산&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;한 상황입니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 과정에서 32번 타일 (1, 4) 이&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;1을 반환&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;한다는 것을 알 수 있죠.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;하지만 초록색 경로를 찾아가는 과정에서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;또다시 32번 타일의 값을 계산&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;하게 됩니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;해당 코드에는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;한 가지 개선점&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 존재합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;바로 각 타일로 이동하는 경로의 수를 구하는 과정에서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;u&gt;&lt;b&gt;동일한 문제를 반복적으로 계산한다&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;는 것이죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;바로 이 부분에서&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;왜 메모이제이션을 사용해야 하는지&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;알 수 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;지금과 같이 목적지부터 출발지까지 경로를 계산하는&amp;nbsp;하향식(Top-Down)으로 문제를 해결한다면 메모이제이션을 통해 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;반복적인 계산 과정을 생략&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;하고&amp;nbsp;보다 효율적으로 문제를 해결할 수 있다는 것이죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;메모이제이션을 사용해 코드를 개선해 보자&lt;/span&gt;&lt;/h2&gt;
&lt;div data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;hr&amp;quot;,&amp;quot;kind&amp;quot;:&amp;quot;hr_type_1&amp;quot;}&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;코드를 어떻게 개선할 수 있을까요?&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;travel(&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;int r, int c&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;함수는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;좌표를 기준으로 각 타일의 값을 계산&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;하기 때문에 계산된 값을 저장하기 위해 파라미터로 전달받은 좌표를 활용할 수 있을 것 같습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;그렇다면 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;2차원 배열&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; (int[][]) 이나 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;해시형 자료 구조&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt; (Map&amp;lt;Point, Integer&amp;gt;) 를 사용해&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;계산된 정보를 저장&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;한다면 어떨까요?&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Main {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 중략

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private static int travel(int r, int c) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (map[r][c] != -1) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return memo[r][c];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;memo[r][c] = 0;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;update(r, c, r + 1, c);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;update(r, c, r - 1, c);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;update(r, c, r, c + 1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;update(r, c, r, c - 1);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return memo[r][c];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private static void update(int r, int c, int tr, int tc) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (map[r][c] &amp;lt; map[tr][tc]) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;map[r][c] += travel(tr, tc);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;travel(&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;int r, int c&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;함수는 더 이상 반복적인 계산이 필요하지 않습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;memo[][]&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;라는 2차원 배열에 이전에 계산한 결과가 있다면 early return을 통해 저장된 값을 반환합니다. 그렇지 않다면&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;update(int r, int c, int tr, int tc)&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&amp;nbsp;함수를 상하좌우 4번 호출하면서 재귀적으로 타일에 도달할 수 있는 경로의 수를 계산하죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;최종적으로 좌표 (r, c)에 해당하는 타일의 값을&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;memo[][] 배열에 저장하고 반환&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;하면서 계산한 값을 기억하고 활용할 수 있는&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;똑똑한 함수&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;로 개선되었습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;입출력 및 패딩 과정&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;을 포함한&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;전체 소스 코드&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;는&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/ASak1104/code2self/blob/main/%EB%B0%B1%EC%A4%80/Gold/1520.%E2%80%85%EB%82%B4%EB%A6%AC%EB%A7%89%E2%80%85%EA%B8%B8/%EB%82%B4%EB%A6%AC%EB%A7%89%E2%80%85%EA%B8%B8.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;color: #2e84b6;&quot;&gt;&lt;b&gt;해당 링크&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;에서 확인하실 수 있습니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;글을 마치며&lt;/span&gt;&lt;/h2&gt;
&lt;div data-app=&quot;{&amp;quot;type&amp;quot;:&amp;quot;hr&amp;quot;,&amp;quot;kind&amp;quot;:&amp;quot;hr_type_1&amp;quot;}&quot;&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;&lt;/div&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이로써 메모이제이션이 무엇인지 그리고 어떤 문제 상황에서 활용할 수 있는지 모두 알아보았습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;사실 메모이제이션은 정말 다양한 문제에서 활용할 수 있는 개념입니다. 본문에서 다룬 케이스는 정말 단순한 문제 상황에 불과하죠.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;수학적인 점화식&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 존재하는 문제에서 메모리에 값을 저장하는 것이 메모이제이션이다&quot;라고 생각할 수 있지만, 백엔드 아키텍처에서 반복적인 API 호출 과정 중&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;b&gt;cache&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 활용해 소비자에게 쾌적한 서비스를 제공하는 것도 어떤 의미에서는 메모이제이션이라고 할 수 있겠죠. 결국, 문제를 해결하기 위해 소프트웨어를 설계하는 과정에서 &lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;State&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;를 활용하는 것이 하나의 해결 방법이 될 수 있다는 것입니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;여기까지가 메모이제이션 이야기였습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;이 글이 저를 포함한 모든 독자분들께 개발자로 성장하는 여정의 밑거름이자 나침반으로써 도움이 되었기를 바랍니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: Noto Serif KR;&quot;&gt;감사합니다.&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>DP</category>
      <category>dynamic programming</category>
      <category>memoization</category>
      <category>다이나믹프로그래밍</category>
      <category>동적계획법</category>
      <category>메모이제이션</category>
      <category>백준 1520번</category>
      <category>백준 내리막길</category>
      <category>자바</category>
      <category>재귀</category>
      <author>코딩몬</author>
      <guid isPermaLink="true">https://kazy.tistory.com/2</guid>
      <comments>https://kazy.tistory.com/2#entry2comment</comments>
      <pubDate>Sat, 3 Aug 2024 21:29:22 +0900</pubDate>
    </item>
  </channel>
</rss>