1. 程式人生 > >演算法設計與分析:K-Similar Strings(Week 5)

演算法設計與分析:K-Similar Strings(Week 5)

學號:16340008

Question:

Strings A and B are K-similar (for some non-negative integer K) if we can swap the positions of two letters in A exactly K times so that the resulting string equals B.

Given two anagrams A and B, return the smallest K for which A and B are K-similar.

Example 1:

Input: A = "ab", B = "ba"
Output: 1

Example 2:

Input: A = "abc", B = "bca"
Output: 2

Example 3:

Input: A = "abac", B = "baca"
Output: 2

Example 4:

Input: A = "aabc", B = "abca"
Output: 2

Note:

  1. 1 <= A.length == B.length <= 20
  2. A and B contain only lowercase letters from the set {'a', 'b', 'c', 'd', 'e', 'f'}

Answer:

題意是:給A,B兩個字串,在A中兩個字母互相交換,求最少交換多少次後能得到B,即相似度K。首先我們可以知道A,B等長,且A必定能通過有限次字母交換後得到B。因此我們為得到最小交換次數,應保證每次交換能修正至少一個或兩個(若能做到)字母。我們可以從A字串左邊開始,對字母(下標為i)作如下判斷:

  1. 若A[i] == B[i],即A與B在該下標處相等,則對下一個字母進行判斷。(保證A[0:i] 與 B[0:i]相等)
  2. 若A[i] != B[i],則在A[i+1:]中找到A[j]與B[i]相同與B[j]不同(否則修正效果為0),記錄所有符合條件的下標j。若A[i]又恰好等於B[j],則可直接交換A[i]和A[j],因為這樣修正效果為2,必是最優的交換之一。交換後,由於A[0:i+1]與B[0:i+1]這i個數相等,因此我們可呼叫函式進行遞迴,對A[i+1:]與B[i+1:]進行對比。呼叫的函式的返回值+1(此次交換)即為所求。
  3. 定義times為len(A)-1,該times必然大於等於所求次數(只要保證每次交換能修正一個,兩完全不相似的字串在times次交換後可相等)。遍歷記錄的每個j,對每個j作如下操作:交換A[i]與A[j],對A[i+1:]與B[i+1:]呼叫函式本身遞迴,對其返回值+1與times比較求更小值賦值給times。然後重新交換A[i]與A[j],開始下一個j的操作。結束對每個j的操作後返回times。
  4. 當字串A長度為1時返回0

這裡用到的搜尋思路在於第三步的判斷,對每個有修正意義交換的狀態向下搜尋,因此搜尋的節點上必然字串A與B越來越短,當字串僅剩長度1時返回0(該字串無需交換)。每個節點都對其子節點的返回次數中取最小值,再加一反饋給上層節點。頂層節點對所有返回值對比取最小即可。演算法看似是BFS,然而實際上並不會在得到最小解時便停止搜尋,仍然會搜尋完可能節點後再比較取最小。

以下為程式碼(python3):

class Solution:
    def kSimilarity(self, A, B):
        """
        :type A: str
        :type B: str
        :rtype: int
        """
        times = 0
        for i in range(len(A)-1):
            if A[i] == B[i]:
                continue

            container = []
            for j in range(i+1, len(A)):
                if A[j] == B[i] and A[j] != B[j]:
                    container.append(j)
                    if A[i] == B[j]:
                        A = A[:i] + A[j] + A[i+1: j] + A[i] + A[j+1:] # swap A[i] and A[j]
                        return 1 + self.kSimilarity(A[i+1:], B[i+1:]) 
            
            times = len(A) - 1

            for j in container:
                A = A[:i] + A[j] + A[i+1: j] + A[i] + A[j+1:] # swap A[i] and A[j]
                times = min(times, 1 + self.kSimilarity(A[i+1:], B[i+1:]))
                A = A[:i] + A[j] + A[i+1: j] + A[i] + A[j+1:] # swap A[i] and A[j]
            
            return times

        return 0

以下程式碼可用於本地測試:

test = Solution()
A = 'aabc'
B = 'abca'
print(test.kSimilarity(A, B))

以下為LeetCode中測試結果: