1. 程式人生 > >[Leetcode] K-Similar Strings

[Leetcode] K-Similar Strings

題目

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

解答

題目要求完成指定的交換所需的最少步數。然而對於怎麼樣才能找到這個最少步數的交換方法,我們沒有任何的先驗經驗,因此,對於這個問題,只能使用遍歷可行空間中所有可行解來實現。因此我們第一個想法是,採用DFS式的遞迴來解決這個問題:

class Solution {
public:
    int
kSimilarity(string A, string B) { if (A == B) { return 0; } // 已正確,不用交換。begin記錄第一個錯誤位置 int begin = 0; while (A[begin] == B[begin]) { ++begin; } // A字母可以交換的位置 vector<int> pos; for (int i = begin + 1; i !=
A.size(); ++i) { if (B[begin] == A[i]) { pos.push_back(i); } } int minTime = INT_MAX; for (auto p : pos) { swap(A[begin], A[p]); int time = kSimilarity(A.substr(begin + 1), B.substr(begin + 1)); if (time < minTime) { minTime = time + 1; } swap(A[begin], A[p]); } return minTime; } };

遞迴的思路是每次把一個正確的字元前移到A字串的頭部,k-similar問題就將變為一個規模更小的(k-1)-similar問題。在程式碼中,我們引入了vector<int>型的pos變數,這個變數儲存A頭部可以交換到的所有位置。

然而這種這份程式碼現在並不能很好的工作–即使是DFS本身,已經出現了大量的可行解,而在搜尋這些可行解的過程中,有很多狀態時被重複搜尋的,這種重複帶來了大量的時間浪費。這啟發我們應該使用一個容器,記錄下已經遍歷過的狀態,這裡我們使用一個雜湊表:unordered_map

class Solution {
private:
    unordered_map<string, int> solution;
public:
    int kSimilarity(string A, string B) {
        if (A == B) {
            return 0;
        }
        if (solution[A + B]) {
            return solution[A + B];
        }
        // 已正確,不用交換。begin記錄第一個錯誤位置
        int begin = 0;
        while (A[begin] == B[begin]) {
            ++begin;
        }
        // A字母可以交換的位置
        vector<int> pos; 
        for (int i = begin + 1; i != A.size(); ++i) {
            if (B[begin] == A[i]) {
                pos.push_back(i);
            }
        }
        int minTime = INT_MAX;
        for (auto p : pos) {
            swap(A[begin], A[p]);
            int time = kSimilarity(A.substr(begin + 1), B.substr(begin + 1));
            if (time < minTime) {
                minTime = time + 1;
            }
            swap(A[begin], A[p]);
        }
        solution[A + B] = minTime;
        return minTime;
    }
};

讀者可能會注意到,這裡我們直接使用了字串A+B作為雜湊表的鍵,這事實上是對儲存空間的一個極大的浪費,因此,網上提出了一種使用long代替string的演算法(C++ DFS + DP + string encoding with detailed explanation, beats 88.71%)。但上述演算法已經能通過題解,此處不作繼續優化。