<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jade_.log</title>
        <link>https://velog.io/</link>
        <description>iOS 개발공부</description>
        <lastBuildDate>Fri, 14 May 2021 13:32:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jade_.log</title>
            <url>https://images.velog.io/images/jade_/profile/895dcafc-99a0-4d83-8d83-c6b0b3b6c77b/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jade_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jade_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Swift] 프로그래머스 - 순위 검색]]></title>
            <link>https://velog.io/@jade_/Swift-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%9C%EC%9C%84-%EA%B2%80%EC%83%89</link>
            <guid>https://velog.io/@jade_/Swift-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%9C%EC%9C%84-%EA%B2%80%EC%83%89</guid>
            <pubDate>Fri, 14 May 2021 13:32:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>문제 : 
카카오는 하반기 경력 개발자 공개채용을 진행 중에 있으며 현재 지원서 접수와 코딩테스트가 종료되었습니다. 이번 채용에서 지원자는 지원서 작성 시 아래와 같이 4가지 항목을 반드시 선택하도록 하였습니다.</p>
</blockquote>
<p>코딩테스트 참여 개발언어 항목에 cpp, java, python 중 하나를 선택해야 합니다.
지원 직군 항목에 backend와 frontend 중 하나를 선택해야 합니다.
지원 경력구분 항목에 junior와 senior 중 하나를 선택해야 합니다.
선호하는 소울푸드로 chicken과 pizza 중 하나를 선택해야 합니다.
인재영입팀에 근무하고 있는 니니즈는 코딩테스트 결과를 분석하여 채용에 참여한 개발팀들에 제공하기 위해 지원자들의 지원 조건을 선택하면 해당 조건에 맞는 지원자가 몇 명인 지 쉽게 알 수 있는 도구를 만들고 있습니다.
예를 들어, 개발팀에서 궁금해하는 문의사항은 다음과 같은 형태가 될 수 있습니다.
코딩테스트에 java로 참여했으며, backend 직군을 선택했고, junior 경력이면서, 소울푸드로 pizza를 선택한 사람 중 코딩테스트 점수를 50점 이상 받은 지원자는 몇 명인가?</p>
<blockquote>
</blockquote>
<p>물론 이 외에도 각 개발팀의 상황에 따라 아래와 같이 다양한 형태의 문의가 있을 수 있습니다.</p>
<blockquote>
</blockquote>
<p>코딩테스트에 python으로 참여했으며, frontend 직군을 선택했고, senior 경력이면서, 소울푸드로 chicken을 선택한 사람 중 코딩테스트 점수를 100점 이상 받은 사람은 모두 몇 명인가?
코딩테스트에 cpp로 참여했으며, senior 경력이면서, 소울푸드로 pizza를 선택한 사람 중 코딩테스트 점수를 100점 이상 받은 사람은 모두 몇 명인가?
backend 직군을 선택했고, senior 경력이면서 코딩테스트 점수를 200점 이상 받은 사람은 모두 몇 명인가?
소울푸드로 chicken을 선택한 사람 중 코딩테스트 점수를 250점 이상 받은 사람은 모두 몇 명인가?
코딩테스트 점수를 150점 이상 받은 사람은 모두 몇 명인가?
즉, 개발팀에서 궁금해하는 내용은 다음과 같은 형태를 갖습니다.</p>
<blockquote>
</blockquote>
<ul>
<li>[조건]을 만족하는 사람 중 코딩테스트 점수를 X점 이상 받은 사람은 모두 몇 명인가?
[문제]
지원자가 지원서에 입력한 4가지의 정보와 획득한 코딩테스트 점수를 하나의 문자열로 구성한 값의 배열 info, 개발팀이 궁금해하는 문의조건이 문자열 형태로 담긴 배열 query가 매개변수로 주어질 때,
각 문의조건에 해당하는 사람들의 숫자를 순서대로 배열에 담아 return 하도록 solution 함수를 완성해 주세요.<blockquote>
</blockquote>
[제한사항]
info 배열의 크기는 1 이상 50,000 이하입니다.
info 배열 각 원소의 값은 지원자가 지원서에 입력한 4가지 값과 코딩테스트 점수를 합친 &quot;개발언어 직군 경력 소울푸드 점수&quot; 형식입니다.
개발언어는 cpp, java, python 중 하나입니다.
직군은 backend, frontend 중 하나입니다.
경력은 junior, senior 중 하나입니다.
소울푸드는 chicken, pizza 중 하나입니다.
점수는 코딩테스트 점수를 의미하며, 1 이상 100,000 이하인 자연수입니다.
각 단어는 공백문자(스페이스 바) 하나로 구분되어 있습니다.
query 배열의 크기는 1 이상 100,000 이하입니다.
query의 각 문자열은 &quot;[조건] X&quot; 형식입니다.
[조건]은 &quot;개발언어 and 직군 and 경력 and 소울푸드&quot; 형식의 문자열입니다.
언어는 cpp, java, python, - 중 하나입니다.
직군은 backend, frontend, - 중 하나입니다.
경력은 junior, senior, - 중 하나입니다.
소울푸드는 chicken, pizza, - 중 하나입니다.
&#39;-&#39; 표시는 해당 조건을 고려하지 않겠다는 의미입니다.
X는 코딩테스트 점수를 의미하며 조건을 만족하는 사람 중 X점 이상 받은 사람은 모두 몇 명인 지를 의미합니다.
각 단어는 공백문자(스페이스 바) 하나로 구분되어 있습니다.
예를 들면, &quot;cpp and - and senior and pizza 500&quot;은 &quot;cpp로 코딩테스트를 봤으며, 경력은 senior 이면서 소울푸드로 pizza를 선택한 지원자 중 코딩테스트 점수를 500점 이상 받은 사람은 모두 몇 명인가?&quot;를 의미합니다.</li>
</ul>
<p>이번 문제는 효율성 테스트까지 포함하므로, 각각의 쿼리에 만족하는 지원자를 info로 부터 하나하나 찾는것은 딱봐도 잘못된 접근이라는 생각이 들었다. 점수를 제외한 쿼리에 부합하는 조건들을 가진 참가자들의 점수를 쉽게 알아낼 수 있다면 좋을 것 같았다. 그렇게 얻어진 점수를 가지고, 쿼리에서 제시하는 점수보다 높은 참가자를 찾는것이 효율적으로 보였다. 각 4가지 영역을 자세히 살펴보면, 각 영역에 해당하는 경우의 수는 개발언어 2개, 지원 직군 2개, 지원 경력 2개, 소울푸드 2개로 총합 16개(4x2x2x2)의 경우의 수가 가능하다. 그래서 먼저 info로 부터, 각 경우의 정보를 뽑아내었다. </p>
<blockquote>
<pre><code>func combination(_ info : [String]) -&gt; [[String] : [Int]] {
    var dic : [[String] : [Int]] = [:] // [조합] : [점수들] 의 딕셔너리
    for personInfo in info {
        var combinations = personInfo.split(separator: &quot; &quot;).map{String($0)}
        let score = combinations.removeLast()
        if dic[combinations] == nil {
            dic[combinations] = [Int(score)!]
        } else {
            dic[combinations]?.append(Int(score)!)
        }
    }
    dic.forEach{dic[$0.key] = $0.value.sorted()}
    return dic
}</code></pre></blockquote>
<pre><code>이 함수는, info를 받아서 [[경우의 수] : [점수] ] 형태의 딕셔너리를 반환한다. 예를 들어서, info 정보로 [&quot;java backend junior pizza 150&quot;] 가 들어오게 되면, [ [&quot;java&quot;,&quot;backend&quot;,&quot;junior&quot;,&quot;pizza&quot;] : [150] ] 형태의 딕셔너리가 반환된다. 문제에서 key값으로 가능한 경우는 총 16개지만, 이 함수에서는 info에 있는 경우만 반환하게 된다.

이런식으로 [경우의 수 : [점수]]형태의 딕셔너리를 구하고 난 후에, 쿼리를 하나씩 돌면서 해당 쿼리에 부합하는 경우의 수를 찾아내고(예를 들어 쿼리 조건이 &quot;java and - and junior and pizza&quot; 이면 가능한 경우의 수는 &quot;java and backend and junior and pizza&quot;, &quot;java and frontend and junior and pizza&quot;  두가지가 된다.) 각 경우의 수에 해당하는 점수 배열을 가져온 후에, 쿼리에서 요구하는 점수보다 높은 점수의 개수를 filter함수를 통해서 구하려고 했었다. 처음에는 그렇게 해서 답을 제출하였는데, 효율성 테스트에서 통과되어지지 않았다. 무엇이 문제인가 생각해보니, 각 쿼리에서 점수 배열을 구했을 때, 점수 배열의 길이가 굉장히 긴 경우에 하나 하나 비교하면서 드는 비용이 굉장히 클것으로 생각이 되었다. 그래서 점수 배열자체를 처음부터 정렬해 놓은 상태로 받아와서, binary search를 통해 쿼리의 점수보다 크거나 같은 인덱스를 구해야 할 것 같다고 생각했다. 먼저 binary search를 위한 재귀함수를 구현하였다.

&gt; ```
func binarySearch(_ sortedArray : [Int], _ low : Int, _ high : Int, _ score : Int, _ idx : inout Int) {
    if sortedArray.count == 1 {
        idx = low
        return
    }
    if low + 1 == high {
        if sortedArray[low] &gt;= score {
            idx = low
        } else {
            idx = high
        }
        return
    }
&gt;     
    let mid = (high + low) / 2
    if sortedArray[mid] &gt;= score { binarySearch(sortedArray, low, mid, score, &amp;idx) }
    else { binarySearch(sortedArray, mid, high, score, &amp;idx) }
}</code></pre><p>위 함수는 정렬되어진 배열을 받아서, score보다 크거나 같은 최소의 인덱스 번호를 찾아서 inout 파라미터인 idx에 그 값을 쓴다. 먼저 배열의 크기가 1인 경우를 고려했다. 재귀를 종료시키는 식은 low와 high가 1차이 나는 경우로(직접 예를 가지고 한번 해보면 쉽게 알 수 있다), 그 경우에 idx값을 결정하고 return하게 된다. 만약 그런 경우가 아니라면 배열의 mid인덱스 값에 따라서 분기하여 재귀한다. 위 함수 사용시의 주의사항은 배열은 항상 정렬되어있어야 하고, 배열의 최댓값(배열의 마지막값)이 score보다 항상 크거나 같아야 한다는 것이다. 이는 함수를 사용할 때 배열의 최댓값과 score를 비교해서 배열의 최댓값이 score보다 작으면 실행하지 않게 하여야 한다는것을 의미한다.(그 경우 쿼리의 점수 조건에 만족하는 경우는 없다)</p>
<p>이제 마지막으로, 각 쿼리에 대해서 조건에 부합하는 경우를 찾고, 그 경우에 score보다 점수가 높거나 같은 지원자가 몇명인지 찾으면 된다. 전체 solution 함수는 다음과 같다.</p>
<blockquote>
<pre><code>func solution(_ info:[String], _ query:[String]) -&gt; [Int] {
    let infoDic = combination(info)
    var result : [Int] = []
    for q in query {
        var count = 0
        var q = q.replacingOccurrences(of: &quot;and&quot;, with: &quot;&quot;).split(separator: &quot; &quot;)
        var available : [[String]] = [] // 가능한 경우의 수
        let score = Int(q.removeLast())!

        for key in infoDic.keys { // 쿼리의 조건에 부합하는 경우의 수를 찾아낸다.
            var flag = 1
            for i in 0...3 {
                if q[i] == &quot;-&quot; { continue }
                if q[i] != key[i] { flag = 0}
            }
            if flag == 1 { available.append(key) }
        }

        guard available.isEmpty == false else {  // 부합하는 조건이 없는 경우, 0을 추가한다.
            result.append(0)
            continue
        }

        for key in available {
            if (infoDic[key]?.last)! &gt;= score {  // s
                var idx = 0
                binarySearch(infoDic[key]!, 0, infoDic[key]!.count-1, score, &amp;idx)
                count = count + infoDic[key]!.count - idx
            }
        }
        result.append(count)
    }
    return result
}</code></pre></blockquote>
<p>```</p>
<blockquote>
</blockquote>
<p>&lt;피드백&gt;
이 문제를 풀기위한 핵심적인 사항은 두가지 인것 같다. 먼저 각 경우의 수로 주어진 대량의 정보를 분류하고, 분류된 정보 내에서 binary search를 통해 원하는 해답을 찾는 것이다. 사실 이 문제를 풀면서 느낀게, 정보를 분류하고 binary search를 사용해야 할 것 같다는 생각을 계속 했었다. 그렇게 해야할 것 같다고 생각했지만 막상 하려고 하니 확신이 안들어서 계속해서 다른 방법을 찾으려고 했고 다른 방법이 생각나지 않고 시간만 계속 흘러서 마지못해 이렇게 구현을 한 것 같다. 확신이 안서더라도 꿋꿋이 시도해보는 힘, 그게 지금 나에게 필요한게 아닌가 싶다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 프로그래머스 - 수식 최대화]]></title>
            <link>https://velog.io/@jade_/Swift-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%98%EC%8B%9D-%EC%B5%9C%EB%8C%80%ED%99%94</link>
            <guid>https://velog.io/@jade_/Swift-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EC%88%98%EC%8B%9D-%EC%B5%9C%EB%8C%80%ED%99%94</guid>
            <pubDate>Sat, 08 May 2021 13:30:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>문제 : 
IT 벤처 회사를 운영하고 있는 라이언은 매년 사내 해커톤 대회를 개최하여 우승자에게 상금을 지급하고 있습니다.
이번 대회에서는 우승자에게 지급되는 상금을 이전 대회와는 다르게 다음과 같은 방식으로 결정하려고 합니다.
해커톤 대회에 참가하는 모든 참가자들에게는 숫자들과 3가지의 연산문자(+, -, <em>) 만으로 이루어진 연산 수식이 전달되며, 참가자의 미션은 전달받은 수식에 포함된 연산자의 우선순위를 자유롭게 재정의하여 만들 수 있는 가장 큰 숫자를 제출하는 것입니다.
단, 연산자의 우선순위를 새로 정의할 때, 같은 순위의 연산자는 없어야 합니다. 즉, + &gt; - &gt; * 또는 - &gt; * &gt; + 등과 같이 연산자 우선순위를 정의할 수 있으나 +,</em> &gt; - 또는 * &gt; +,-처럼 2개 이상의 연산자가 동일한 순위를 가지도록 연산자 우선순위를 정의할 수는 없습니다. 수식에 포함된 연산자가 2개라면 정의할 수 있는 연산자 우선순위 조합은 2! = 2가지이며, 연산자가 3개라면 3! = 6가지 조합이 가능합니다.
만약 계산된 결과가 음수라면 해당 숫자의 절댓값으로 변환하여 제출하며 제출한 숫자가 가장 큰 참가자를 우승자로 선정하며, 우승자가 제출한 숫자를 우승상금으로 지급하게 됩니다.</p>
</blockquote>
<p>예를 들어, 참가자 중 네오가 아래와 같은 수식을 전달받았다고 가정합니다.</p>
<blockquote>
</blockquote>
<p>&quot;100-200*300-500+20&quot;</p>
<blockquote>
</blockquote>
<p>일반적으로 수학 및 전산학에서 약속된 연산자 우선순위에 따르면 더하기와 빼기는 서로 동등하며 곱하기는 더하기, 빼기에 비해 우선순위가 높아 * &gt; +,- 로 우선순위가 정의되어 있습니다.
대회 규칙에 따라 + &gt; - &gt; * 또는 - &gt; * &gt; + 등과 같이 연산자 우선순위를 정의할 수 있으나 +,* &gt; - 또는 * &gt; +,- 처럼 2개 이상의 연산자가 동일한 순위를 가지도록 연산자 우선순위를 정의할 수는 없습니다.
수식에 연산자가 3개 주어졌으므로 가능한 연산자 우선순위 조합은 3! = 6가지이며, 그 중 + &gt; - &gt; * 로 연산자 우선순위를 정한다면 결괏값은 22,000원이 됩니다.
반면에 * &gt; + &gt; - 로 연산자 우선순위를 정한다면 수식의 결괏값은 -60,420 이지만, 규칙에 따라 우승 시 상금은 절댓값인 60,420원이 됩니다.</p>
<blockquote>
</blockquote>
<p>참가자에게 주어진 연산 수식이 담긴 문자열 expression이 매개변수로 주어질 때, 우승 시 받을 수 있는 가장 큰 상금 금액을 return 하도록 solution 함수를 완성해주세요.</p>
<blockquote>
</blockquote>
<p>[제한사항]
expression은 길이가 3 이상 100 이하인 문자열입니다.
expression은 공백문자, 괄호문자 없이 오로지 숫자와 3가지의 연산자(+, -, <em>) 만으로 이루어진 올바른 중위표기법(연산의 두 대상 사이에 연산기호를 사용하는 방식)으로 표현된 연산식입니다. 잘못된 연산식은 입력으로 주어지지 않습니다.
즉, &quot;402+-561</em>&quot;처럼 잘못된 수식은 올바른 중위표기법이 아니므로 주어지지 않습니다.
expression의 피연산자(operand)는 0 이상 999 이하의 숫자입니다.
즉, &quot;100-2145*458+12&quot;처럼 999를 초과하는 피연산자가 포함된 수식은 입력으로 주어지지 않습니다.
&quot;-56+100&quot;처럼 피연산자가 음수인 수식도 입력으로 주어지지 않습니다.
expression은 적어도 1개 이상의 연산자를 포함하고 있습니다.
연산자 우선순위를 어떻게 적용하더라도, expression의 중간 계산값과 최종 결괏값은 절댓값이 263 - 1 이하가 되도록 입력이 주어집니다.
같은 연산자끼리는 앞에 있는 것의 우선순위가 더 높습니다.</p>
<p>문제를 보면서 수식을 계산하는 거라 자료구조 시간에 배운 후위 표현식을 이용해 문자열로 된 식을 계산했던 것이 생각났다. 여기서도 그렇게 할 수는 있지만, 굳이 그렇게 해야할 필요가 있는가에 대해서 잠시 고민해본 후, 그렇게 할 필요가 없다고 느꼈다. 우선순위에 대한 경우의 수가 6가지니깐, 각 경우의 수에 대해서 식을 계산하기만 하면 된다. </p>
<blockquote>
</blockquote>
<p>&lt;풀이 전략&gt;</p>
<ol>
<li>일단 입력으로 들어온 값을, 숫자와 수식으로 분리시켜 배열에다가 담는다. </li>
<li>6가지의 우선순위를 적용해 각 식을 계산한다. 식을 계산하는 방법은, 식에 있는 우선순위가 높은 연산자부터 찾아서 왼쪽항과 오른쪽항을 그 연산자를 이용해 계산한다.</li>
</ol>
<blockquote>
</blockquote>
<p>&lt;구현&gt;</p>
<ol>
<li>입력으로 들어온 값을 숫자와 수식으로 분리해서 [String] 형태로 반환하는 함수 작성
예) 
입력 : &quot;234+4*5&quot; 
출력 : [&quot;234&quot;, &quot;+&quot;, &quot;4&quot;, &quot;5&quot;]<blockquote>
</blockquote>
<pre><code>func separate(_ expression : String) -&gt; [String] {
 var tmp = &quot;&quot;
 var result : [String] = []
 for ch in expression {
     let ch = String(ch)
     if ch == &quot;+&quot; || ch == &quot;-&quot; || ch == &quot;*&quot; {
         result.append(tmp)
         tmp = &quot;&quot;
         result.append(ch)
     } else {
         tmp += ch
     }
 }
 result.append(tmp)
&gt;     
 return result
}</code></pre><blockquote>
</blockquote>
</li>
<li>1번에서 분리한 [String] 형태의 표현식과 [&quot;+&quot;, &quot;-&quot;, &quot;*&quot;]과 같이 연산자 우선순위가 나열된 [String]을 받아서 계산된 결과를 반환하는 함수 작성(유의사항 : 절대값을 반환해야함)<blockquote>
</blockquote>
<pre><code>func calc(_ expression : [String], priority : [String]) -&gt; Int64 {
 var expression = expression
&gt;     
 for op in priority {
     while ( expression.contains(op) ) {  // operator가 포함되어 있으면 그 오퍼레이트 실행
         let idx = expression.firstIndex(of: op)!
         let operand = expression.remove(at: idx+1)
         switch op {
         case &quot;+&quot;:
             expression[idx-1] = String(Int(expression[idx-1])! + Int(operand)!)
         case &quot;-&quot;:
             expression[idx-1] = String(Int(expression[idx-1])! - Int(operand)!)
         default:
             expression[idx-1] = String(Int(expression[idx-1])! * Int(operand)!)
         }
         expression.remove(at: idx)
     }
 }
 return abs(Int64(expression.first!)!)
}</code></pre>연산자에 대해서, 그 연산자가 표현식에 포함되어 있다면, 그 연산자의 위치를 구한다. 연산자는 그 인덱스의 이전 원소와 이후 원소를 연산시키면 된다. 이전 원소에 계산한 값을 덮어쓰고, 연산항과 연상항의 오른쪽 요소는 제거해야 한다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] 프로그래머스 - 괄호 변환]]></title>
            <link>https://velog.io/@jade_/Swift-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B4%84%ED%98%B8-%EB%B3%80%ED%99%98</link>
            <guid>https://velog.io/@jade_/Swift-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4-%EA%B4%84%ED%98%B8-%EB%B3%80%ED%99%98</guid>
            <pubDate>Sun, 02 May 2021 16:47:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
</blockquote>
<p>문제 : 
카카오에 신입 개발자로 입사한 &quot;콘&quot;은 선배 개발자로부터 개발역량 강화를 위해 다른 개발자가 작성한 소스 코드를 분석하여 문제점을 발견하고 수정하라는 업무 과제를 받았습니다. 소스를 컴파일하여 로그를 보니 대부분 소스 코드 내 작성된 괄호가 개수는 맞지만 짝이 맞지 않은 형태로 작성되어 오류가 나는 것을 알게 되었습니다.
수정해야 할 소스 파일이 너무 많아서 고민하던 &quot;콘&quot;은 소스 코드에 작성된 모든 괄호를 뽑아서 올바른 순서대로 배치된 괄호 문자열을 알려주는 프로그램을 다음과 같이 개발하려고 합니다.</p>
<blockquote>
</blockquote>
<p>&lt;용어의 정의&gt;
&#39;(&#39; 와 &#39;)&#39; 로만 이루어진 문자열이 있을 경우, &#39;(&#39; 의 개수와 &#39;)&#39; 의 개수가 같다면 이를 균형잡힌 괄호 문자열이라고 부릅니다.
그리고 여기에 &#39;(&#39;와 &#39;)&#39;의 괄호의 짝도 모두 맞을 경우에는 이를 올바른 괄호 문자열이라고 부릅니다.
예를 들어, &quot;(()))(&quot;와 같은 문자열은 &quot;균형잡힌 괄호 문자열&quot; 이지만 &quot;올바른 괄호 문자열&quot;은 아닙니다.
반면에 &quot;(())()&quot;와 같은 문자열은 &quot;균형잡힌 괄호 문자열&quot; 이면서 동시에 &quot;올바른 괄호 문자열&quot; 입니다.
&#39;(&#39; 와 &#39;)&#39; 로만 이루어진 문자열 w가 &quot;균형잡힌 괄호 문자열&quot; 이라면 다음과 같은 과정을 통해 &quot;올바른 괄호 문자열&quot;로 변환할 수 있습니다.</p>
<blockquote>
</blockquote>
<ol>
<li>입력이 빈 문자열인 경우, 빈 문자열을 반환합니다. </li>
<li>문자열 w를 두 &quot;균형잡힌 괄호 문자열&quot; u, v로 분리합니다. 단, u는 &quot;균형잡힌 괄호 문자열&quot;로 더 이상 분리할 수 없어야 하며, v는 빈 문자열이 될 수 있습니다. </li>
<li>문자열 u가 &quot;올바른 괄호 문자열&quot; 이라면 문자열 v에 대해 1단계부터 다시 수행합니다. 
3-1. 수행한 결과 문자열을 u에 이어 붙인 후 반환합니다. </li>
<li>문자열 u가 &quot;올바른 괄호 문자열&quot;이 아니라면 아래 과정을 수행합니다. 
4-1. 빈 문자열에 첫 번째 문자로 &#39;(&#39;를 붙입니다. 
4-2. 문자열 v에 대해 1단계부터 재귀적으로 수행한 결과 문자열을 이어 붙입니다. 
4-3. &#39;)&#39;를 다시 붙입니다. 
4-4. u의 첫 번째와 마지막 문자를 제거하고, 나머지 문자열의 괄호 방향을 뒤집어서 뒤에 붙입니다. 
4-5. 생성된 문자열을 반환합니다.
&quot;균형잡힌 괄호 문자열&quot; p가 매개변수로 주어질 때, 주어진 알고리즘을 수행해 &quot;올바른 괄호 문자열&quot;로 변환한 결과를 return 하도록 solution 함수를 완성해 주세요.<blockquote>
</blockquote>
&lt;매개변수 설명&gt;
p는 &#39;(&#39; 와 &#39;)&#39; 로만 이루어진 문자열이며 길이는 2 이상 1,000 이하인 짝수입니다.
문자열 p를 이루는 &#39;(&#39; 와 &#39;)&#39; 의 개수는 항상 같습니다.
만약 p가 이미 &quot;올바른 괄호 문자열&quot;이라면 그대로 return 하면 됩니다.</li>
</ol>
<p>처음 문제를 읽으면서 드는 생각이, 도대체 이게 무슨 말인지 뭘 구하라는 건지 이해가 도통 되지 않아서 당황했었던것 같다. 잠시 고민한 결과, 그냥 문제에서 제공한 절차대로 데이터를 가공하는 함수를 만들어야겠다는 생각이 들었다.</p>
<blockquote>
<p>&lt;풀이 전략&gt; 
문제에 있는 절차 그대로를 구현하는 함수를 작성하자.</p>
</blockquote>
<p>일단 핵심적으로 구현해야 할 함수는 두가지로 정했다. 하나는 입력이 올바른 문자열인지를 판단하는 함수이고, 다른 하나는 문제의 절차를 그대로 따라서 균형잡힌 괄호 문자열을 받아 올바른 문자열을 출력하는 함수이다. 먼저 입력이 올바른 문자열인지를 판단하는 함수는 다음과 같다. </p>
<blockquote>
<pre><code>func arePairsMatched(_ p : String) -&gt; Bool {
    if p.isEmpty { return true } 
    var p = p
    var stack : [String] = []
    while p.isEmpty == false {
        let x = String(p.removeFirst())
        if stack.isEmpty { stack.append(x) }
        else if stack.last! == &quot;(&quot; &amp;&amp; x == &quot;)&quot;
            { stack.removeLast() }
        else { stack.append(x) }
    }
    return stack.isEmpty
}</code></pre></blockquote>
<pre><code>입력으로 문자열을 받아서 문자열이 올바른 괄호 문자열이면 true, 아니면 false를 반환한다. 동작원리는, 먼저 스택을 위한 배열을 하나 만들고, 입력으로 들어온 p에서 첫번째 요소를 하나씩 빼서 스택에 넣는다. 이 때 만약 스택에서 &quot;(&quot;와 &quot;)&quot;이 한 쌍씩 맞춰지면 스택에서 그것들을 뺀다. 만약 앞선 작업을 모두 수행하였을 때, 스택이 비었다면 올바른 문장이다.(한 쌍씩 모두 맞춰져서 스택에서 빠져나갔으므로)

이제 문제의 절차를 그대로 따르는 함수를 작성해줄것인데, 코드는 다음과 같다.
&gt; ```
func transform(_ p : String) -&gt; String {
&gt; 
        if p.isEmpty { return &quot;&quot;}   // 1번
        else if arePairsMatched(p) { return p }
&gt;         
        var v = p
        var u = String(v.removeFirst())
&gt;         
        while u.filter{$0 == &quot;(&quot;}.count != u.filter{$0 == &quot;)&quot;}.count{ // 2번
            let x = v.removeFirst()
            u.append(x)
        }
        if arePairsMatched(u) { // 3번
            return u + transform(v)
        } else {   // 4번
            var tmp = &quot;(&quot; + transform(v) + &quot;)&quot;
            u.removeFirst()
            u.removeLast()
            u = u.map{
                if $0 == &quot;(&quot; { return &quot;)&quot; }
                else { return &quot;(&quot; }
            }.reduce(&quot;&quot;){$0 + $1}
            return tmp + u
        }
    }</code></pre><p>다른 과정들은 딱히 헷갈리거나 어려운 과정은 없고 그대로 로직을 구현해 주면 된다. 2번에서 p를 두 균형잡힌 문자열 u와 v로 분리하는데, 조건에서는 u는 더이상 분리할 수 없는 균형잡힌 괄호 문자열이다. 그 말은 즉슨, 입력 p의 앞부분을 따서 만들 수 있는 최소 개수를 가진 균형잡힌 괄호 문자열이라는 말이다. 그래서 u가 균형잡힌 괄호 문자열이 될 때 까지(u의 &quot;(&quot;개수와 &quot;)&quot;개수가 다르면) p의 첫번 째 요소를 계속 제거하면서 u에 넣어주었다. 이 때 주의할 점은 초기에 u가 비어있을 때는 &quot;(&quot;의 개수와 &quot;)&quot;의 개수가 동일하다고 판단해버리니 시작하기 전에 p의 첫번째 요소를 u에 넣고 시작해야 한다는 것이다.</p>
<p>&lt;문제 총평 및 피드백&gt;
이번 문제에서 요구하는 능력은 주어진 조건을 올바른 로직으로 잘 구현해 낼 수 있는지와 재귀함수를 잘 이해하고 있는지에 대한 내용인 것 같다. 
프로그래머에게 있어서 문제에 대한 올바른 로직을 프로그래밍 언어로 구현하는 것은 기본적인 능력이고, 내가 생각하는 더 중요하고 고민해보아야 하는 부분은 같은 로직이라도 어떻게 더 가독성이 좋고 효율적인 코드로 만들것인가에 대한 내용이다.</p>
]]></description>
        </item>
    </channel>
</rss>