[搜索算法系列] —— 雙向搜索

字串變換問題

已知兩個字串a、b,兩個數組from、to,變換規則爲:from[i]能夠轉換爲to[i]。問a到b須要通過多少次變換?若10步內a沒法變換到b,輸出'NO ANSWER!'。

例如對於a = 'abcd',b = 'xyz',from = ['abc', 'ud', 'y'],to = ['xu', 'y', 'yz']'abcd' -> 'xud' -> 'xy' -> 'xyz'須要經歷3次變換。python

該題的解決方案使用到了廣度優先搜索,咱們將通過一次變換可以獲得的全部字符串做爲一種狀態,而後判斷是否存在目標字符串,不存在則計算下一層狀態,存在則當前層數爲最小變換次數。數組

能夠獲得以下代碼。app

def solution(a, b, f, t):
    def extend(h, q):
        s, n = h
        for i in range(len(s)):
            for j in range(len(f)):
                if f[j] != s[i:i+len(f[j])]:
                    continue
                q.append([s[:i] + t[j] + s[i+len(f[j]):], n+1])
    queue = [[a, 0]]
    while queue:
        head = queue.pop(0)
        if head[0] == b: return head[1]
        extend(head, queue)
    return 'NO ANSWER!'

若是該題目沒有時間限制,這樣寫固然沒有問題。post

考慮一個字符串的一次變換的全部可能,與字符串的長度和變換規則有關,這個中間狀態可能數量很是龐大,那咱們如何對該代碼進行優化呢?優化

若是你閱讀過個人上一篇文章可能會想到經過拆分問題規模來分別求解,以達到下降問題數量級的目的。而在本題中,咱們分別從a和b同時開始搜索,若是它們的搜索路徑相遇了,即找到最小變換次數,經過雙向搜索的方式,咱們將規模爲2N的問題,分解爲了兩個規模爲N的問題。code

def solution(a, b, f, t):
    ans = float('inf')
    qa, qb, da, db = [a], [b], {a:0}, {b:0}
    level = 0

    def extend(q, d, f, t):
        s = q.pop(0)
        for i in range(len(s)):
            for j in range(len(f)):
                if f[j] != s[i:i+len(f[j])]:
                    continue
                tmp = s[:i] + t[j] + s[i+len(f[j]):]
                q.append(tmp)
                d[tmp] = d[s]+1

    while qa and qb and level <= 10:
        while qa and da[qa[0]] == level:
            extend(qa, da, f, t)
        while qb and db[qb[0]] == level:
            extend(qb, db, t, f)
        level += 1
        for k in da.keys():
            ans = min(ans, da[k] + db.get(k, float('inf')))
        if ans < float('inf'): return ans
    return 'NO ANSWER!'

該段代碼中,開闢了兩個隊列和兩個字典,分別存儲雙向搜索狀態和字串所在層數。每一次隊列擴展整一層的數據,用level來記錄當前層數。擴展完成後判斷兩個字典是否有公共鍵值,若是有則說明發現了搜索路徑的交點,對應層數相加即轉換次數。隊列

本文示例題目與acwing 190.字串變換一致,讀者可自行嘗試提交,驗證本身代碼的正確性。ip

相關文章
相關標籤/搜索