티스토리 뷰

코드카타

 

가장 가까운 글자

 

문제

 

 

정답

class Solution {
    fun solution(s: String): IntArray {
        var res = IntArray(s.length) { -1 }
        var arr = s.toCharArray() 
        var sMap = mutableMapOf<Char, Int>()
        for (i in arr.indices) { 
            if (sMap.containsKey(arr[i])) {
                res[i] = i - sMap[arr[i]]!!
            }
            sMap[arr[i]] = i 
        }
        return res
    }
}

res의 모든 요소를 -1로 초기화 해주고 s를 Char배열로 받아온다.

문자와 위치값을 저장할 맵을 만들어주고 containsKey()를 써 문자 중복 여부를 확인한 뒤

있다면 현재 i에서 위치값을 빼주고 res에 넣어준다.

 

 

변수선언

var res = IntArray(s.length) { -1 }

연산을 수행하고 난 결과를 담을 배열에 해당하는 변수이다.


문자열 s의 길이만큼의 크기를 가진 IntArray를 선언해준다.

해당 배열의 요소들은 모두 배열 선언문 옆 중괄호에 들어가는 값인 -1로 초기화 된다.

 

var sArr = s.toCharArray()

// s = banana
// arr = [b, a, n, a, n, a]

주어진 문자열을 문자 단위로 접근하여 각 문자의 인덱스를 알아내기 위해

toCharArray()함수를 이용해 문자열을 문자배열로 변환해 준다.

 

var sMap = mutableMapOf<Char, Int>()

문자(Char)를 키로, 정수(Int)를 값으로 가지는 가변형 맵을 선언해준다.

 

 

문제로직

for (i in sArr.indices) {
    if (sMap.containsKey(sArr[i])) {
        res[i] = i - sMap[sArr[i]]!! // 같은 글자끼리의 거리값을 res에 저장
    }
    sMap[sArr[i]] = i // 처음들어오는 문자의 위치값을 저장
}

i는 0 ~ sArr.size -1의 값을 가진다.

 

sMap에서 s의 i번째 문자에 해당하는 키(Char)가 없다면 

sMap에서 해당 문자의 키값을 i(Int)로 초기화한다.

 

맵에 해당 키가 존재한다면 res의 i번지값을

i에서 현재 문자의 위치값을 뺀 값으로 초기화 해준다.

 

sMap의 키값은 Int?형에 해당하므로 '!!'로 Null 체크를 해줘야 한다.

 

 

함수형 프로그래밍

class Solution {
    fun solution(s: String): List<Int> {
        return s.withIndex()
            .map { (i, c) -> s.slice(0 until i).lastIndexOf(c).let {
                if (it != -1) i - it else it } }
    }
}

한 번에 정리해서 설명하자니 설명이 너무 난잡해질 거 같아

이해가 쉽도록 코드를 분할해서 로직을 설명하겠다.

 

val s = "abc"
val indexedString = s.withIndex()
for ((index, char) in indexedString) {
    println("${index}번지 값: $char")
}

'''
0번지 값: a
1번지 값: b
2번지 값: c
'''

위의 코드에서 withIndex는 문자열 s에 대해 이터레이션을 수행한다.

 

val s="banana"
print(s.withIndex().map { (i, c) -> 
	s.slice(0 until i).lastIndexOf(c) }) // [-1, -1, -1, 1, 2, 3]

해당 print구문은 문자열을 0부터 i미만까지 슬라이스한 배열에서

lastIndexOf(c)로 c가 마지막으로 나온 위치를 반환한 값이 요소로 들어간 배열을 반환한다.

 

lastIndexOf 함수는 해당 문자가 문자열에 하나만 존재하는 경우 -1을 반환한다.

 

[-1, -1, -1, 1, 2, 3].let {if (it != -1) i - it else it } }

it의 값이 -1이 아니라면 이전에 같은 문자가 나타났다는 뜻이므로

i에서 it을 빼주고 나온적이 없다면 it(-1)을 반환한다.

 


 

질문지 화면 기능 구현

 

프래그먼트 생성

companion object {
    private const val ARG_QUESTION_TYPE = "questionType"

    fun newInstance(questionType: Int):QuestionFragment{
        val fragment = QuestionFragment()
        val args = Bundle()
        args.putInt(ARG_QUESTION_TYPE, questionType)
        fragment.arguments = args
        return fragment
    }
}

companion object는 클래스의 인스턴스화 없이 직접 접근할 수 있다.

 

프래그먼트의 인스턴스를 생성하는 함수 newInstance는 QuestionFragment를 상속한다.

 

함수내에서 fragment와 데이터를 담을 args를 선언한다.

아규먼트에 페이저 번호 키값을 추가함으로써 새로운 페이지의 페이지 번호를 전달한다.

 프래그먼트의 arguments를 args로 초기화 해준 뒤 프래그먼트를 반환한다.

 

 

동작 방식

override fun createFragment(position: Int): Fragment {
    return QuestionFragment.newInstance(position)
}

 TestAcitivy의 ViewPager에서 새로운 페이지를 부를 때, 해당 페이저에서 createFragment를 호출하게 된다.

 

createFragment의 인자값으론 현재 페이지의 위치가 들어오게 되고,

해당 인자값은 아까 설명한 newInstance()함수의 인자값으로 들어가게 된다.

 

최종적으로는 생성되는 프래그먼트의 아규먼트에 페이지 번호 키값이 추가 된다.

 

 

리스트 변수 선언

private var questionType: Int = 0

    private val questionTitle = listOf(
        R.string.question1_title,
        R.string.question2_title,
        R.string.question3_title,
        R.string.question4_title,
    )

    private val questionTexts = listOf(
        listOf(R.string.question1_1, R.string.question1_2, R.string.question1_3),
        listOf(R.string.question2_1, R.string.question2_2, R.string.question2_3),
        listOf(R.string.question3_1, R.string.question3_2, R.string.question3_3),
        listOf(R.string.question4_1, R.string.question4_2, R.string.question4_3),
    )

    private val questionAnswers = listOf(
        listOf(
            listOf(R.string.question1_1_answer1, R.string.question1_1_answer2),
            listOf(R.string.question1_2_answer1, R.string.question1_2_answer2),
            listOf(R.string.question1_3_answer1, R.string.question1_3_answer2)
        ),
        listOf(
            listOf(R.string.question2_1_answer1, R.string.question2_1_answer2),
            listOf(R.string.question2_2_answer1, R.string.question2_2_answer2),
            listOf(R.string.question2_3_answer1, R.string.question2_3_answer2)
        ),
        listOf(
            listOf(R.string.question3_1_answer1, R.string.question3_1_answer2),
            listOf(R.string.question3_2_answer1, R.string.question3_2_answer2),
            listOf(R.string.question3_3_answer1, R.string.question3_3_answer2)
        ),
        listOf(
            listOf(R.string.question4_1_answer1, R.string.question4_1_answer2),
            listOf(R.string.question4_2_answer1, R.string.question4_2_answer2),
            listOf(R.string.question4_3_answer1, R.string.question4_3_answer2)
        ),
    )

string.xml 파일을 기반으로 필요한 변수들을 선언해준다.

 

 

 

onCreate()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    arguments?.let{
        questionType = it.getInt(QuestionFragment.ARG_QUESTION_TYPE)
    }
}

인자값이 null이 아니라면 questionType에서 현재 몇 번째 화면인지 받아온다.

 

 

 

질문지 레이아웃 그리기

 

layout에 fragment_question.xml 파일을 추가해준다.

 

android:padding="26dp"

화면 전체를 감싸는 위젯에 패딩값을 준다.

 

 

 

밝은 붉은색으로 칠한 테두리 부분이 패딩값이 적용된 부분이다.

위젯을 배치할 수 있는 공간이 모든방향으로 26씩 줄어들었다.

 

해당 패딩 속성은 위젯을 중앙으로 모아주는 역할을 한다.

 

제목과 질문들이 들어있는 LinearLayout은 위쪽으로 붙게 배치하고,

다음 버튼은 아래쪽으로 붙게 배치한다.

 

 

라디오 버튼 추가

<RadioGroup
android:id="@+id/rg_answer_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<RadioButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="14sp"
    android:text="@string/question1_1_answer1" />

<RadioButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="14sp"
    android:text="@string/question1_1_answer2" />

</RadioGroup>

각 질문마다 선택지는 2개이므로 RadioGroup안에 RadioButton을 2개씩 추가해준다.

참고로 라디오 버튼은 여러 개의 옵션 중에서 하나만 선택할 수 있는 동그란 모양의 버튼을 뜻한다.

 

 

wrap_content와 match_parent

 

wrap_content는 해당 뷰가 포함하는 내용물의 크기에 따라 동적으로 크기가 조정되고

match_parent는 해당 뷰가 부모 컨테이너의 크기와 동일하게 되도록 크기가 조정된다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함