본문 바로가기
카테고리 없음

회문 검사 프로그램 만들기

by 경자꿈사 2024. 12. 16.

어떤 문자열이 회문인지 검사하는 프로그램을 만들어 보자.
회문은 '돌아올 회(回)'와 '글 문(文)'이라는 한자로 구성된 한자어로 똑바로 읽어도 거꾸로 읽어도 똑같은 문장을 말한다.
영어로는 Palindrome인데, 영어나 한자어나 둘 다 낯설긴 마찬가지다.
큰 인기를 끌었던 드라마 '이상한 변호사 우영우'를 보면 주인공이 처음 보는 사람에게 자신을 소개할 때 '기러기 토마토 스위스 인도인 별똥별 우영우'라고 하는데 
주인공 이름 '우영우'를 포함해 모든 단어가 회문이다.

쉽게 생각하면 대상 문자열의 역 문자열을 만들고 원래 문자열과 비교해서 같으면 회문이라고 보면 된다.
이미 String 클래스에 reverse라는 역 문자열을 반환해 주는 메서드가 존재하지만 학습을 위해 직접 만들어 보자.

아래는 문자열 하나를 받아서 그것의 역 문자열을 반환해 주는 메서드를 두 가지 방식으로 작성한 코드이다.
reverse_by_append 메서드는 downto 메서드를 사용해서 대상 문자열의 마지막 문자부터 첫 번째 문자까지 한 문자씩 새로운 빈 문자열의 끝에 추가하여 역 문자열을 만든다.
reverse_by_insert 메서드는 each_char 메서드로 앞에서부터 문자 하나씩 순회하면서 새로운 빈 문자열의 시작 위치에 문자들을 삽입해서 역 문자열을 만든다.
insert 메서드는 내부적으로 문자열이 삽입되는 위치를 포함한 이후의 문자들을 이동시켜야 하는 작업이 필요하므로, 성능적인 면에서 reverse_by_insert보다는 reverse_by_append 메서드가 더 낫다.

def reverse_by_append(str)
  r_str = ""
  (str.size - 1).downto(0) { |i| r_str << str[i] }
  r_str
end

def reverse_by_insert(str)
  r_str = ""
  str.each_char { |ch| r_str.insert(0, ch) }
  r_str
end

테스트를 위해 먼저 D:/blog/ruby 폴더 아래 palindrome 폴더를 만들고 그 안에 palindrome.rb 파일을 만들어 앞의 코드를 저장하자.
그리고 같은 폴더의 위치에서 irb를 실행하여 다음 그림처럼 예제 코드를 입력해 보자.

두 메서드 모두 잘 동작하는 걸 볼 수 있다.
이제 역 문자열을 만들어 돌려주는 메서드를 만들었으니 실제 회문인지 검사해 주는 메서드를 만들어 보자.
아래 코드를 보면 인수로 받은 문자열과 해당 문자열의 역 문자열이 같은지 비교하는 아주 간단한 코드이다.

def palindrome?(str)
  str == reverse_by_append(str)
end

앞의 코드를 palindrome.rb 파일에 추가하여 저장한 후 다음 그림처럼 irb를 실행하여 바로 테스트를 해보자.

'ruby', 'level', 'noon', 'refer' 4개의 단어 중 3개의 단어가 회문이라고 결과가 잘 나오는 것을 볼 수 있다.
이제 역 문자열을 만들지 않고 회문인지 검사하는 방법을 생각해 보자.
이것도 별로 어렵지 않은데, 예를 들어 문자열의 길이가 4라고 한다면 첫 번째 문자와 끝에서 첫 번째 문자가 서로 같고 두 번째 문자와 끝에서 두 번째 문자가 서로 같으면 회문이다.
즉, (문자마다 폭이 갖다고 가정하고) 문자열을 종이에 출력해서 반으로 접었을 때 서로 맞닿는 문자끼리 비교해서 모두 같으면 회문이라고 보면 된다.
문자열의 길이가 홀수라면 가운데 짝이 없는 문자가 하나 남는데 회문 여부랑은 상관이 없으므로 신경 쓰지 않아도 된다.
아래 이러한 생각을 코드로 옮겨보았다.
문자열 또는 배열에서 음의 정수를 인덱스로 사용하면 끝에서부터 요소를 조회할 수 있기 때문에 앞쪽의 문자와 비교할 뒤쪽의 문자를 찾기 위해 str[-1 - n] 처럼 작성할 수 있다.
첫 번째 블록 실행 때는 str[0]과 str[-1]을 비교하고, 두 번째 블록 실행 때는 str[1]과 str[-2]를 비교하는 식이다.

def palindrome?(str)
  result = true
  (str.size / 2).times do |n| 
    result = str[0 + n] == str[-1 - n]
    break if !result
  end
  result
end

palindrome.rb 파일에 있는 기존 palindrome? 메서드의 코드는 주석 처리하고(주석 처리가 꼭 필요하지는 않다), 새로 작성한 palindrome? 메서드의 코드를 파일에 추가하고 저장하자.
다시 irb를 실행한 후 다음 그림처럼 우영우의 소개말 안에 포함된 모든 단어에 대해 회문 여부를 검사해 보자.

결과를 보면, 예상대로 우영우의 소개말 안에 있는 모든 단어가 '회문'임을 알 수 있다.

See you again~~