Trouble Shooting

문자열 원하는 길이 만큼씩 자르려면 어떻게 해야할까? - swift

나른한코딩 2021. 7. 17. 17:46

Trouble

  • 코딩테스트 문제를 푸는 중,
    일정한 크기로 문자열을 나눠야하는 경우가 생겼는데 separator(공백, 특정문자 등)를 기준으로 자르는 경우는 해봤지만 원하는 크기로 일정하게 잘라본 적은 없는 것 같다..
  • 2시간 가까이 머리를 싸매고 검색도 했지만 원하는 결과를 찾지는 못했다.
  • 대신 문자열의 인덱스를 이용하여 일부 문자열을 얻는 방법에서 착안하여 작성해보았다.

생각한 방법

  • index( , offsetBy: Int) 함수를 사용하였다.
  • 문자열의 시작인덱스(newStr의 0번 인덱스)와 끝인덱스(시작인덱스로부터 offset만큼 떨어진 원소의 인덱스)를 구한다.
  • newStr의 start부터 end까지를 result배열에 append 한다.
  • newStr을 앞에서부터 offset만큼의 문자를 제거한다.
  • 문자열의 갯수가 offset보다 작을 때까지 반복한다.
  • newStr의 가장 마지막 문자열의 문자수가 offset보다 작은 경우에 나머지 문자들을 result배열에 추가하도록 if 문을 추가한다.
func cutString(_ str: String, _ offset: Int) -> [String] {
  var newStr = str
  var result: [String] = []
  while newStr.count > offset {
    let start = newStr.startIndex
    let end = newStr.index(newStr.startIndex, offsetBy: offset)
    // let temp = newStr[start..<end]
    // result.append(String(temp))
    result.append(String(newStr[start..<end]))

    
    for _ in 0..<offset {
      newStr.removeFirst()
    }
    
    if newStr.count < offset {
      result.append(newStr)
    }
  }
  return result
}

cutString("abcdefg", 2) // ["ab", "cd", "ef", "g"]




String, Character, String.Index 타입에 대해 자세히 알고 싶다면 다음 링크를 참고해주세요!
String타입은 왜 인덱스를 통해 문자에 바로 접근할 수 없을까?



To-Do🤔 : 시간복잡도를 줄이는 방법 고민해보기


- 위에 작성한 코드는 while문과 for문이 중첩되어 있으므로 시간복잡도는 O(n^2) 이다.
- 시간복잡도를 O(n)으로 만들기 위해서 생각한 방법은
- 1) offset만큼 자른 뒷부분 문자열을 인자로 넘겨주어 재귀적으로 함수를 호출하도록 구현한다.
- 2) while문 안에 index와 result 변수를 활용해 아래와 같이 해결한다.

재귀로 구현하는 것보다는 코드 라인수가 길어질 수는 있으나, 직관적으로 쉬운 아이디어로 풀 수 있었다.
아래와 같이 구현하면 while문 하나만 사용되었으므로 O(n)의 시간복잡도로 구현이 가능하다.

(코드에 대한 설명은 주석을 첨부하였습니다.)

func cutString(_ str: String, offset: Int) -> [String] {
    let arr: [String] = str.map { String($0) } // 문자열을 문자 하나하나가 담긴 배열로 변형
    var answer = [String]()
    
    var index: Int = 0
    var result: String = ""
    
    if offset == str.count { // offset과 str.count가 같다면, 바로 asnwer배열에 추가
        answer.append(str)
    } else {
        while true {
            if index == arr.count { // index가 arr의 마지막 원소의 인덱스 일때
                if result == "" { // result가 비어있다면 break를 통해 while문을 벗어난다.
                    break
                } else { // result에 값이 있다면 (ex. 아래의 offset을 5를 준 경우 처럼 마지막에 5자리 이하인 "ge" 가 result에 들어 있는 경우)
                    answer.append(result) // result에 담긴 자투리를 answer에 추가하고 break를 통해 while문을 벗어난다.
                    break
                }
            }
            
            // 이하는 index가 arr.count에 도달하지 않은 경우이다.
            result += arr[index] // result에 arr[index] 번째를 추가
            
            if result.count == offset { // result 문자열이 offset 길이가 되면(즉, 자르길 원하는 길이가 되면)
                answer.append(result) // answer에 append 해주고
                result = "" // result를 초기화 한다.
            }
            
            index += 1 // while문을 돌때마다 index값을 증가시키면서 arr의 다음원소에 대해 while문을 반복한다.
        }
    }
    
    return answer
}

print(cutString("heafaf", offset: 2)) // ["he", "af", "af"]
print(cutString("hasdfsdfsdge", offset: 5)) // ["hasdf", "sdfsd", "ge"]
print(cutString("hasd", offset: 5)) // ["hasd"]




틀린부분이 있거나, 더 좋은 방법이 있다면 댓글로 남겨주세요!
🌈댓글은 언제나 환영입니다🙏🏻

반응형