문제

명우는 홍준이와 함께 팰린드롬 놀이를 해보려고 한다.

먼저, 홍준이는 자연수 N개를 칠판에 적는다. 그 다음, 명우에게 질문을 총 M번 한다.

각 질문은 두 정수 S와 E(1 ≤ S ≤ E ≤ N)로 나타낼 수 있으며, S번째 수부터 E번째 까지 수가 팰린드롬을 이루는지를 물어보며, 명우는 각 질문에 대해 팰린드롬이다 또는 아니다를 말해야 한다.

예를 들어, 홍준이가 칠판에 적은 수가 1, 2, 1, 3, 1, 2, 1라고 하자.

  • S = 1, E = 3인 경우 1, 2, 1은 팰린드롬이다.
  • S = 2, E = 5인 경우 2, 1, 3, 1은 팰린드롬이 아니다.
  • S = 3, E = 3인 경우 1은 팰린드롬이다.
  • S = 5, E = 7인 경우 1, 2, 1은 팰린드롬이다.

자연수 N개와 질문 M개가 모두 주어졌을 때, 명우의 대답을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 수열의 크기 N (1 ≤ N ≤ 2,000)이 주어진다.

둘째 줄에는 홍준이가 칠판에 적은 수 N개가 순서대로 주어진다. 칠판에 적은 수는 100,000보다 작거나 같은 자연수이다.

셋째 줄에는 홍준이가 한 질문의 개수 M (1 ≤ M ≤ 1,000,000)이 주어진다.

넷째 줄부터 M개의 줄에는 홍준이가 명우에게 한 질문 S와 E가 한 줄에 하나씩 주어진다.

출력

총 M개의 줄에 걸쳐 홍준이의 질문에 대한 명우의 답을 입력으로 주어진 순서에 따라서 출력한다. 팰린드롬인 경우에는 1, 아닌 경우에는 0을 출력한다.

내가 푼 풀이

- 처음에는 주어진 질문에대한 모든 경우의 수에서 팰린드롬인지 구하려고 했지만, 수열의 크기를 감안했을 때 시간초과가 날 거라 생각했다.

- dp를 이용하여 dp 배열에 저장하고, 질문들을 해당 배열에서 꺼내오려한다.

- dp에 저장하기 위해 팰린드롬의 성질을 좀 더 생각해봐야 했다.

- 여기서 이용했던 팰린드롬의 성질은 주어진 수열에서 s번째부터 e까지의 수열이 팰린드롬이라면, s+1번째부터 e-1까지의 수열도 역시 팰린드롬이 된다.

- 예로 위에 주어진 수열 1, 2, 1, 3, 1, 2, 1에서 0번째부터 6번째까지의 수열이 1, 2, 1, 3, 1, 2, 1이고, 해당 수열은 팰린드롬이다.

- 0번째부터 6번째까지의 수열이 팰린드롬이라면, 1번째부터 5번째까지의 수열도 역시 팰린드롬이다. ( 2, 1, 3, 1, 2 )

- 위의 성질을 이용하기 위해선 주어진 수열의 길이가 최소 3이어야 한다.

- 수열의 길이가 1이라면 원소 자기 자신하나로 팰린드롬이 된다.

- 수열의 길이가 2라면 원소 하나와 그다음 원소가 같다면, 팰린드롬이 된다.

- 수열의 길이가 3 미만인 경우의 수는 위와 같이 구하고, 3 이상인 경우는 아래와 같이 구한다.

- dp [i][j] = i번째부터 j번째까지의 수열이 팰린드롬인가? ( 팰린드롬이라면 1을 저장, 아니라면 0을 저장)

- 위 성질을 이용하여 수열의 길이가 3이상이라면, 두가지 경우의 수를 판단하면된다.

1. 수열의 양 끝 원소가 같은지?

2. 주어진 수열의 s+1번째부터 e-1까지의 수열이 팰린드룸인지?

 

해당 문제는 많은 입력과 출력을 요구하므로, readLine을 이용한 입력및 print 출력을 남용하면 시간초과가 뜬다.

더 빠른 FileIO를 이용하여 입력 및 한 문자열에 저장하여 한번에 출력하는 방식을 이용한다.

 

코드로 구현해보자.

import Foundation

// Swift 입출력
final class FileIO {
    private var buffer:[UInt8]
    private var index: Int
    
    init(fileHandle: FileHandle = FileHandle.standardInput) {
        buffer = Array(fileHandle.readDataToEndOfFile())+[UInt8(0)]
        index = 0
    }
    
    @inline(__always) private func read() -> UInt8 {
        defer { index += 1 }
        
        return buffer.withUnsafeBufferPointer { $0[index] }
    }
    
    @inline(__always) func readInt() -> Int {
        var sum = 0
        var now = read()
        var isPositive = true
        
        while now == 10
                || now == 32 { now = read() }
        if now == 45{ isPositive.toggle(); now = read() }
        while now >= 48, now <= 57 {
            sum = sum * 10 + Int(now-48)
            now = read()
        }
        
        return sum * (isPositive ? 1:-1)
    }
    
    @inline(__always) func readString() -> String {
        var str = ""
        var now = read()
        
        while now == 10
                || now == 32 { now = read() }
        
        while now != 10
                && now != 32 && now != 0 {
            str += String(bytes: [now], encoding: .ascii)!
            now = read()
        }
        
        return str
    }
}

// 입력
let fileIO = FileIO()
let N = fileIO.readInt()
var arr = [Int]()
for i in 0..<N {
    arr.append(fileIO.readInt())
}
let M = fileIO.readInt()
var dp = Array(repeating: Array(repeating: 0, count: N), count: N)

// 수열의 길이가 1일때
for i in 0..<N {
    dp[i][i] = 1
}

// 수열의 길이가 2일때
for i in 0..<N-1 {
    if arr[i] == arr[i+1] {
        dp[i][i+1] = 1
    }
}

// 수열의 길이가 3이상일때
for i in (0..<N-2).reversed() {
    for j in (i+2..<N).reversed() {
        let idx = j
        if arr[i] == arr[idx] && dp[i+1][idx-1] == 1 {
            dp[i][idx] = 1
        }
    }
}
var answer = ""
for i in 0..<M {
    let s1 = fileIO.readInt()
    let s2 = fileIO.readInt()
    answer += "\(dp[s1-1][s2-1])\n"
}

print(answer)

+ Recent posts