호우동의 개발일지

Today :

article thumbnail

문제 이해 단계

https://school.programmers.co.kr/learn/courses/30/lessons/12904?

팰린드롬이란 앞뒤를 뒤집어도 똑같은 문자열을 뜻한다.

입력으로 문자열 s가 주어질 때,
s의 부분 문자열 중에 가장 긴 팰린드롬의 길이를 구하는 문제

 


문제 접근 단계

제한사항부터 살펴보자.

문자열의 길이 s는 2,500 이하의 자연수이고, 알파벳 소문자로만 구성되어 있다.
팰린드롬이라는 좌우대칭 규칙성을 판단할 때는 보통 스택을 사용한다.

이 문제에서 사용하려고 했는데,
각 자리마다 스택을 사용하여 넣었다 뺐다 하는 것은 굉장히 비효율적이다.

그리고 예전에 백준 포스팅에서 이런 유사한 문제를 다룬 적이 있다.

https://howudong.tistory.com/319

 

[C++] 백준 10942 - 팰린드롬?

문제 이해 단계 https://www.acmicpc.net/problem/10942 10942번: 팰린드롬? 총 M개의 줄에 걸쳐 홍준이의 질문에 대한 명우의 답을 입력으로 주어진 순서에 따라서 출력한다. 팰린드롬인 경우에는 1, 아닌 경

howudong.tistory.com

여기서는 팰린드롬의 길이를 구하는 것은 아니지만,
일정 구간이 팰린드롬인지를 판단하는 문제이다.

사실 이 문제의 개념도 해당 문제와 동일하다.

위 링크와이 문제에서 핵심이 되는 로직
위 링크와이 문제에서 핵심이 되는 로직

이 핵심 개념을 그대로 따라간다.

d [i][j] = k → i번째 인덱스부터 j번째 인덱스까지의 팰린드롬의 최대 개수는 k이다.
라고 하자.

인덱스가 0이 아닌 1부터 시작한다고 가정하고  d [1][7]을 살펴보자.

첫 번째 인덱스부터 마지막 인덱스까지,
즉 전체 문자열이 팰린드롬이 되려면 어떻게 돼야 할까?

1. 첫 번째 원소와 마지막 원소가 서로 같아야 한다.
2. 첫 번째 원소와 마지막 원소를 뺀 문자열로 이뤄져 있는 것이 팰린드롬이어야 한다.

이 두 가지를 만족해야 한다.

여기서 첫 번째 원소와 마지막 원소를 뺀 문자열이라는 것은? d [2][6]이다.
한마디로 d [2][6]이 팰린드롬이어야 된다는 것이다.

그럼 d [2][6]이 팰린드롬이 판단하는 법은?
d [1][7]이랑 똑같은 방법으로 판단한다.

똑같은 방식으로 하다 보면 가장 오른쪽 그림처럼 된다.
(1번 조건에서 아무도 걸리지 않을 때를 가정)

원소가 하나일 때는 팰린드롬이고, 길이는 1이다.
그러면 d [3][5]는 팰린드롬이 맞다는 것을 확신할 수 있다.

길이는 어떻게 되는가? 당연히 d [4][4] + 2를 하면 된다.
팰린드롬은 두 개씩 비교하여 짝을 맞춘 것이기 때문이다.

최종적으로 점화식은 이렇게 나오게 된다.

if(arr [i] == arr [j] && dp [i][j-1]!= -1) 
dp [i][j] = dp [i+1][j-1] + 2


else dp [i][j] = -1

여기서 -1은 팰린드롬이 아님을 뜻한다.

이를 bottom-up 방식이든 top-down 방식이든 본인이 편한 대로 구현하면 된다.

이제 문제 구현단계에서 C#과 C++ 코드로 모두 구현해 보자.

 

 


문제 구현 단계


C++로 구현한 코드

더보기
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int dp[2501][2501] = {0,};
int arr[2501] = {0,};
int solution(string s)
{
    int answer=0;
    for(int i = 0; i < s.length(); i++) arr[i+1] = s[i];
    
    for(int i = s.length(); i > 0; i--){
        for(int j = i; j <= s.length(); j++){
        	// 원소가 1개일때는 무조건 팰린드롬, 길이가 1
            if(i == j) dp[i][j] = 1;
            
            // 점화식대로 해줌
            else if(arr[i] == arr[j] && dp[i+1][j-1] != -1){
                dp[i][j] = dp[i+1][j-1] + 2;
            }
            else dp[i][j] = -1;
            
            // 길이가 가장 긴 것을 정답으로
            answer = max(answer,dp[i][j]);
        }
    }

    return answer;
}

 


C#으로 구현한 코드

더보기
using System;
public class Solution {
    static int[,] dp = new int[2501,2501];
    static char[] arr = new char[2501];
    public int solution(string s) {
        for(int i = 0; i < s.Length; i++) arr[i+1] = s[i];
        
        int answer = 0;
        for(int i = s.Length; i > 0; i--){
            for(int j = i; j <= s.Length; j++){
            	// 원소가 1개 일때는 무조건 팰린드롬, 길이가 1
                if(i == j) dp[i,j] = 1;
                // 점화식대로 해줌
                else if(arr[i] == arr[j] && dp[i+1,j-1] != -1){
                    dp[i,j] = dp[i+1,j-1]+2;
                }
                else dp[i,j] = -1;
                
                // 길이가 가장 긴 것을 정답으로
                answer = Math.Max(answer,dp[i,j]);
            }
            
        }

        return answer;
    }
}

두 코드 다 별로 다를 것이 없다.
dp 문제이기 때문에 풀이는 매우 간단하다.

여기서 팰린드롬이 아님을 뜻함을 -1로 표현한 이유는
i와 j가 1이 차이 날 때를 대비한 것이다.

만약 0을 팰린드롬이 아님을 의미하게 한다면 i와 j가 연속될 때 예외가 발생한다.

dp [1][2]를 점화식을 적용하면 d [2][1]이 되기 때문에 0이 나오는데
이때 팰린드롬이어도 팰린드롬이 아니게 될 수 있기 때문이다.

풀이는 여기까지이다.

 

 


시행착오

백준에서 예전에 풀어본 문제와 굉장히 유사했기 때문에 푸는데 문제는 없었다.

게임 서버 프로그래밍에 익숙해지기 위해
프로그래머스로 넘어오고 알고리즘을 C#으로 풀기 시작했는데, 아직 어색하기는 하다.

파이팅

생활비..