解讀易位構詞偵測

演算法分析

Agenda

  • 導言/基本介紹
  • 介紹與說明各個解法
  • 結論

易位構詞?

  • http://www.wikiwand.com/en/Anagram
  • 將字母拆分重組得到一個新詞
  • 本例中我們把問題先簡單化

定義問題

  • 我們會取得兩個字串
  • 這兩個字串長度會相同
  • 這兩個字串只會由26個小寫英文字母組成
  • 要能判斷這兩個字串是否符合易位構詞

發想解法

  • 因為易位構詞是拆分重組
  • 我們來把第一個字串拆掉
  • 組出不同可能組法
  • 有一個對到第二個字串就表示符合!

暴力破解

  • 教材中的第三種解法
  • 找出並儲存所有結果再比對
  • 很單純所以不太可能出錯
  • 可是所有結果的數量是字串長度的階乘
  • 表示這個方法很貴,超過
O(2^n)
O(2n)
class Node:
    """
    Use a node to represent possibility of next letter
    """
    def __init__(self, n):
        self.value = n
        self.children = []
        
    
    def set_child(self, child):
        self.children.append(child)
    
    
    def get_child(self, n):
        for child in self.children:
            if child.value == n: return child
        return None
        
        
class  Condition:
    """
    A class to supply a ability to retrieve letters
    """
    def __init__(self):
        self.root = Node('root')
        
        
    def build_all_condition_node(self, current_node, arr):
        if len(arr) < 2:
            current_node.set_child(Node(arr[0]))
            return
        for c in arr:
            temp_node = Node(c)
            current_node.set_child(temp_node)
            temp_arr = arr[:]
            temp_arr.remove(c)
            self.build_all_condition_node(temp_node, temp_arr)
        
    
    def is_match(self, other_string):
        temp_node = self.root
        for c in other_string:
            if not temp_node.get_child(c):
                return False
            else:
                temp_node = temp_node.get_child(c)
        return True
    
    
    def build(self, target_string):
        all_letter_list = list(target_string)
        temp_node = self.root
        self.build_all_condition_node(temp_node, all_letter_list)
            
        
    
            

condition = Condition()
condition.build('earth')
condition.is_match('heart')

有必要嗎

  • 我們在找出了所有可能後,只需要其中"一個"
  • 而且我們要比完才能知道不符合
  • 好像很浪費......

換個想法

  • 我們其實只要確認:
  • 第二個字串沒有第一個字串不存在的字元
  • 各個字元的數量也要相同
  • 那麼......

逐個檢查

  • 教材中的第一種解法
  • 逐個取出第一個字串的字元進行以下:
  • 逐個比對第二個字串
  • 如果值相同:
    跳出此次比對,將該位置的值設為None,避免重覆造成誤判
    換下一個第一個字串的字元進行比對
  • 如果此次比對找不到相同值:表示不符
  • 若比完所有字串沒有不符合,即判定為符合

善用一下教材

好像好多了

  • Quick Fail減輕比對失敗的挫敗感
  • 不需要另外儲存可能字串的空間
  • 最差的狀態下是
O(n^2)
O(n2)

如果不管順序呢

  • 這個方法裡複雜的部份在循序再比對
  • 因為兩個字串其實有著相同的字元
  • 是否可以先將兩個字串用同樣的規則先行排序
  • 預期要得到相同的結果,以判斷是不是符合

排序比對

  • 教材中的第二種解法
  • 先將兩個字串排序
  • 如果相等就符合,否則就不符合
  • Python提供sorted(iterable) method可使用
  • 會傳回list
  • 教材中使用list.sort()
     

兩步就完成?

  • 沒有那麼神,sort本身也有cost
  • 分析時要記得看到函數使用要展開
  • sort視其演算法大概落在
O(n^2)
O(n2)
O(n\log n)
O(nlogn)

還可以怎麼想

  • 這個解法中我們把原字串都改變了
  • 原來不一定要從原始資料來判斷
  • 甚至判斷的根據不用是字串
  • 可以藉由一種物件是否有相同值的屬性來發想

 計數比對

  • 教材中的第四種解法
  • 將兩個字串分別轉成有26個元素的清單
  • 元素位置對應字母序
  • 元素值對應字母出現次數
  • 如果兩個清單元素都相等代表兩個字串有相同數量的相同字元
  • 反之如果有個元素有不同值則兩字串有不一樣的字元
  • 與我們的需求相符

 再次的善用教材

好像很強 

  • 連排序都不用,時間複雜度是線性
  • 多使用的空間只有兩個清單

還能更好嗎

  • 嗯......你的好是指?

結論

AnAnagramDetectionExample

By Ted Wu

AnAnagramDetectionExample

  • 1,606