본문 바로가기

Develop/Algorithm

[Algorithm] Python3 Boggle 문제 풀이

 

Boogle 문제 링크 :https://algospot.com/judge/problem/read/BOGGLE

 

algospot.com :: BOGGLE

보글 게임 문제 정보 문제 보글(Boggle) 게임은 그림 (a)와 같은 5x5 크기의 알파벳 격자인 게임판의 한 글자에서 시작해서 펜을 움직이면서 만나는 글자를 그 순서대로 나열하여 만들어지는 영어 단어를 찾아내는 게임입니다. 펜은 상하좌우, 혹은 대각선으로 인접한 칸으로 이동할 수 있으며 글자를 건너뛸 수는 없습니다. 지나간 글자를 다시 지나가는 것은 가능하지만, 펜을 이동하지않고 같은 글자를 여러번 쓸 수는 없습니다. 예를 들어 그림의 (b), (c

algospot.com

 

프로그래밍 대회에서 배우는 알고리즘 문제해결전략 의 6장에 보면 

 

무식하게 풀기의 예시로 Boggle 문제가 나온다.

 

이 문제의 정답률이 15%인 것이, 풀고싶어진 계기가 되었다.

 

 

Boggle 문제는 아래 (a) 와 같이 5x5의 글자 판이 주어지며, 

 

각 글자 판당 N개의 단어가 주어지고 해당 글자 판에서 단어들의 존재 여부를 파악해야 하는 문제이다.

 

중복 탐색도 허용하나 자기 자신을 계속 참조할 수는 없다. 

 

예를들어 (d) REPEAT의 경우 E가 두번 참조된 것을 볼 수 있다.

 

 

풀이

이 문제를 풀기 전에 개인적으로 생각했던 제약 조건은

 

1. try 문을 쓰지 않고 풀기 

 

 이유 : try except 문을 쓴다는 것은 에러가 발생할 상황을 프로그래머가 어느정도 알고 있다는 것인데, 

 

      그러면 그냥 그 에러가 나는 코드라인을 고치던가, 아니면 애초에 그런 에러가 안생기게 만들어야 한다. 

   

2. flag 변수를 만들어서 다른 재귀를 종료시킬 수 있어야 한다.

  

  이유 : BOGGLE 문제의 댓글을 보다보니, 시간초과를 내는 인풋을 보고 얻은 아이디어이다.

 

          무식하게 푼다는 건 종료 조건 없이 모든 경우의 수를 따져보는 것인데 해당 문제의 복잡도는

 

          O(3n )과 O(8n )의 사이라고 생각된다. 왜냐면 양 사각 테두리에서는 찾을 수 있는 경우의 수가 3가지이고, 

 

         테두리 안에 있는 대상은 8가지의 경우가 있기 때문이다.

 

          따라서 엄청나게 복잡한 문제이며, 종료조건 없이 파이썬으로만 풀게 되면 시간 초과가 발생할 수 있다.

 

          시간 초과가 발생할 수 있는 경우는 아래와 같다. 

 

문제를 풀때 생각했던 아이디어

 

1. 각각의 좌표에서 갈 수 있는 곳이 한정되어 있음

  

아래 그림을 보면 꼭지점 부분에서는 서치할 수 있는 장소가 크게 3 군데만 존재한다.

꼭지점의 서치 경우의 수

 

 

반면에 꼭지점을 제외한 테두리 부분은 서치 장소가 5군데만 존재한다.

 

꼭지점을 제외한 테두리의 서치 경우의 수

마지막으로 가운데 있는 부분은 서치 장소가 총 8 군데 존재한다.

 

중앙의 서치 경우의 수

따라서 해당 경우의 수를 계산하기 위해 서치해야 하는 해당 장소의 X,Y좌표를 받고,

그 좌표에 맞게 검색을 진행하면 된다.

 

2. input으로 들어오는 string을 미리 스크리닝 한다.

 

 이유 : 들어오는 input string이 보드판에 없는 경우, 굳이 서치를 하지 않아도 되기 때문

 

 

구현

구현시의 특이점 

 

1. global_counter를 선언하여, 모든 재귀 함수가 돌아가는 와중에 하나라도 완성본이 나온다면,

나머지를 모두 종료시켰음 이를 위해 return True 를 반환시키는 조건을 추가함.

 

2. result = all(elem in board_index for elem in word) 구문을 통해 단어들이 실제로 보드에 있는지 확인하였음.

 

TEST = int(input())
# index [x,y]
def finder(location,counter):  
    global word_length
    global word
    global global_counter
    
    three = [[0,0],[0,4],[4,0],[4,4]]
    five = [[0,1],[0,2],[0,3],[1,0],[2,0],[3,0],[4,1],[4,2],[4,3],[1,4],[2,4],[3,4]]
    if global_counter:
        return True
    if word_length == counter:
        global_counter+=1
        return True
    
    if location:
        if location in three:
            if location ==[0,0]:
                # compare three time
                if word[counter] == board[0][1]:
                    finder([0,1],counter+1)
                if word[counter] == board[1][0]:
                    finder([1,0],counter+1) 
                if word[counter] ==board[1][1]:
                    finder([1,1],counter+1)
                else:
                    return False
                
            elif location == [0,4]: 
               # compare three times 
                if word[counter] == board[0][3]:
                    finder([0,3],counter+1)
                if word[counter] == board[1][3]:
                    finder([1,3],counter+1)
                if word[counter] ==board[1][4]:
                    finder([1,4],counter+1)
                else:
                    return False
    
            elif location == [4,0]:
               # compare three times 
                if word[counter] == board[3][0]:
                    finder([3,0],counter+1)
                if word[counter] == board[4][1]:
                    finder([4,1],counter+1)
                if word[counter] ==board[3][1]:
                    finder([3,1],counter+1)
                else:
                    return False
            elif location == [4,4]:
                if word[counter] == board[4][3]:
                    finder([4,3],counter+1)
                if word[counter] == board[3][3]:
                    finder([3,3],counter+1)
                if word[counter] ==board[3][4]:
                    finder([3,4],counter+1)
                else:
                    return False
        elif location in five:
            if location == [0,1]:
                if word[counter] == board[0][0]:
                    finder([0,0],counter+1)
                if word[counter] == board[0][2]:
                    finder([0,2],counter+1)
                if word[counter] ==board[1][0]:
                    finder([1,0],counter+1)
                if word[counter] ==board[1][1]:
                    finder([1,1],counter+1)
                if word[counter] ==board[1][2]:
                    finder([1,2],counter+1)
                else:
                    return False
            elif location == [0,2]:
                if word[counter] == board[0][1]:
                    finder([0,1],counter+1)
                if word[counter] == board[0][3]:
                    finder([0,3],counter+1)
                if word[counter] ==board[1][1]:
                    finder([1,1],counter+1)
                if word[counter] ==board[1][2]:
                    finder([1,2],counter+1)
                if word[counter] ==board[1][3]:
                    finder([1,3],counter+1)
                else:
                    return False       
            elif location == [0,3]:
                if word[counter] == board[0][2]:
                    finder ([0,2],counter+1)
                if word[counter] == board[0][4]:
                    finder ([0,4],counter+1)
                if word[counter] ==board[1][2]:
                    finder ([1,2],counter+1)
                if word[counter] ==board[1][3]:
                    finder ([1,3],counter+1)
                if word[counter] ==board[1][4]:
                    finder ([1,4],counter+1)
                else:
                    return False
            elif location == [1,0]:
                if word[counter] == board[0][0]:
                    finder([0,0],counter+1)
                if word[counter] == board[0][1]:
                    finder([0,1],counter+1)
                if word[counter] ==board[1][1]:
                    finder([1,1],counter+1)
                if word[counter] ==board[2][0]:
                    finder([2,0],counter+1)
                if word[counter] ==board[2][1]:
                    finder([2,1],counter+1)
                else:
                    return False
            elif location == [2,0]:
                if word[counter] == board[1][0]:
                    finder([1,0],counter+1)
                if word[counter] == board[1][1]:
                    finder([1,1],counter+1)
                if word[counter] ==board[2][1]:
                    finder([2,1],counter+1)
                if word[counter] ==board[3][0]:
                    finder([3,0],counter+1)
                if word[counter] ==board[3][1]:
                    finder([3,1],counter+1)
                else:
                    return False
            elif location == [3,0]:
                if word[counter] == board[2][0]:
                    finder([2,0],counter+1)
                if word[counter] == board[2][1]:
                    finder([2,1],counter+1)
                if word[counter] ==board[3][1]:
                    finder([3,1],counter+1)
                if word[counter] ==board[4][0]:
                    finder([4,0],counter+1)
                if word[counter] ==board[4][1]:
                    finder([4,1],counter+1)
                else:
                    return False
            elif location == [4,1]:
                if word[counter] == board[4][0]:
                    finder([4,0],counter+1)
                if word[counter] == board[4][2]:
                    finder([4,2],counter+1)
                if word[counter] ==board[3][0]:
                    finder([3,0],counter+1)
                if word[counter] ==board[3][1]:
                    finder([3,1],counter+1)
                if word[counter] ==board[3][2]:
                    finder([3,2],counter+1)
                else:
                    return False
            elif location == [4,2]:
                if word[counter] == board[4][1]:
                    finder([4,1],counter+1)
                if word[counter] == board[4][3]:
                    finder([4,3],counter+1)
                if word[counter] ==board[3][1]:
                    finder([3,1],counter+1)
                if word[counter] ==board[3][2]:
                    finder([3,2],counter+1)
                if word[counter] ==board[3][3]:
                    finder([3,3],counter+1)
                else:
                    return False
            elif location == [4,3]:
                if word[counter] == board[4][2]:
                    finder([4,2],counter+1)
                if word[counter] == board[4][4]:
                    finder([4,4],counter+1)
                if word[counter] ==board[3][2]:
                    finder([3,2],counter+1)
                if word[counter] ==board[3][3]:
                    finder([3,3],counter+1)
                if word[counter] ==board[3][4]:
                    finder([3,4],counter+1)
                else:
                    return False
            elif location == [1,4]:
                if word[counter] == board[0][4]:
                    finder([0,4],counter+1)
                if word[counter] == board[0][3]:
                    finder([0,3],counter+1)
                if word[counter] ==board[1][3]:
                    finder([1,3],counter+1)
                if word[counter] ==board[2][3]:
                    finder([2,3],counter+1)
                if word[counter] ==board[2][4]:
                    finder([2,4],counter+1)
                else:
                    return False
            elif location == [2,4]:
                if word[counter] == board[1][3]:
                    finder([1,3],counter+1)
                if word[counter] == board[1][4]:
                    finder([1,4],counter+1)
                if word[counter] ==board[2][3]:
                    finder([2,3],counter+1)
                if word[counter] ==board[3][3]:
                    finder([3,3],counter+1)
                if word[counter] ==board[3][4]:
                    finder([3,4],counter+1)
                else:
                    return False
            elif location == [3,4]:
                if word[counter] == board[2][3]:
                    finder([2,3],counter+1)
                if word[counter] == board[2][4]:
                    finder([2,4],counter+1)
                if word[counter] ==board[3][3]:
                    finder([3,3],counter+1)
                if word[counter] ==board[4][3]:
                    finder([4,3],counter+1)
                if word[counter] ==board[4][4]:
                    finder([4,4],counter+1)
                else:
                    return False

        else:
            if word[counter] == board[location[0]-1][location[1]-1]:
                finder([location[0]-1,location[1]-1],counter+1)
            if word[counter] == board[location[0]-1][location[1]]:
                finder([location[0]-1,location[1]],counter+1)
            if word[counter] == board[location[0]-1][location[1]+1]:
                finder([location[0]-1,location[1]+1],counter+1)
            if word[counter] == board[location[0]][location[1]-1]:
                finder([location[0],location[1]-1],counter+1)
            if word[counter] == board[location[0]][location[1]+1]:
                finder([location[0],location[1]+1],counter+1)
            if word[counter] == board[location[0]+1][location[1]-1]:
                finder([location[0]+1,location[1]-1],counter+1)
            if word[counter] == board[location[0]+1][location[1]]:
                finder([location[0]+1,location[1]],counter+1)
            if word[counter] == board[location[0]+1][location[1]+1]:
                finder([location[0]+1,location[1]+1],counter+1)
            else:
                return False
for _ in range(TEST):
    board = []
    # board maker
    index = []
    # getting board
    for _ in range(5):
        temp = input()
        board.append(list(temp))
        index+=list(temp)
    #setting board_index to minimize check
    board_index = set(index)
    
    word_count = int(input())
    for _ in range(word_count):
        raw = input()
        word = list(raw)
        # quick index
        global_counter =0
        word_length = len(word)
        
        result =  all(elem in board_index  for elem in word)

        if result:
            for x in range(5):
                if not global_counter :
                    for y in range(5):
                        if not global_counter:
                            # first word exists
                            if board[x][y]==word[0]:
                                out1 = finder([x,y],1)
                            if global_counter:
                                print("%s YES" % raw)
                                break
                                                
            if not global_counter:
                print("%s NO" % raw)
        
        else:
            print("%s NO" % raw)

 

 

56ms면 python으로 푼 사례중 상당히 상위권에 속한다. 

'Develop > Algorithm' 카테고리의 다른 글

[Algorithm] Python3 JUMPGAME 문제 풀이  (0) 2019.12.16