LeetCode.859-夥伴字符串(Buddy Strings)

這是悅樂書的第330次更新,第354篇原創

01 看題和準備

今天介紹的是LeetCode算法題中Easy級別的第200題(順位題號是859)。給定兩個字母A和B的小寫字母,當且僅當我們可以在A中交換兩個字母以使結果等於B時返回true。例如:

輸入:A =“ab”,B =“ba”
輸出:true


輸入:A =“ab”,B =“ab”
輸出:false


輸入:A =“aa”,B =“aa”
輸出:true


輸入:A =“aaaaaaabc”,B =“aaaaaaacb”
輸出:true


輸入:A =“”,B =“aa”
輸出:false

注意

  • 0 <= A.length <= 20000

  • 0 <= B.length <= 20000

  • A和B僅由小寫字母組成。

本次解題使用的開發工具是eclipse,jdk使用的版本是1.8,環境是win7 64位系統,使用Java語言編寫和測試。

02 錯誤的嘗試

一次錯誤的提交,原因是理解題目出現偏差,我以爲是相鄰的兩個字符進行替換,但是不然,是任意兩個字符進行替換能夠和B相等,就可以返回true。例如:A="abab",B="abab",下面的方法計算的結果是false,但其實應該是true,因爲只要將A中的第一個a換到第三個位置,將第三個位置的a換到第一個位置,就能保證A和B相等,交換b也是一樣的。再例如A="aaabac",B="aaacab",也是一樣的情況。

public boolean buddyStrings(String A, String B) {
    if (A.length() != B.length()) {
        return false;
    }
    boolean result = false;
    char[] arr = A.toCharArray();
    int n = arr.length;
    for (int i=0; i<n-1; i++) {
        char temp = arr[i];
        arr[i] = arr[i+1];
        arr[i+1] = temp;
        if (B.equals(String.valueOf(arr))) {
            return true;
        } else {
            char temp2 = arr[i];
            arr[i] = arr[i+1];
            arr[i+1] = temp2;
        }
    }
    return result;
}


03 第一種解法

我們再來好好分析下題目,既然不是交換相鄰的兩個字母,那麼就需要分三種情況來具體分析:

  1. A和B的長度不相等。兩者的長度不相等,無論A怎麼換字母,都不可能變成B,直接返回false。

  2. A和B的長度相等,並且A和B相等。要想滿足A換兩字母后能夠等於B,A就必須至少要有一個字母出現兩次以上,因爲只有交換重複出現的字母,A纔不會變,也就可以和B相等了。例如,A="aa",B="aa",或者A="afag",B="afag",這兩組都是滿足條件。再例如,A="abc",B="abc",雖然A等於B,但是A無論交換哪兩個字母,交換後的新A都不會等於B。

  3. A和B的長度相等,但是A和B不相等。同樣,要想A交換兩個字母后等於B,前提條件就是在對應索引位置上A不等於B的字母的個數,只能是2個,多於2個或少於2個都不行。同時還要滿足:A中第一個不等於B的字母要等於B中不等於A的第二個字母,A中第二個不等於B的字母要等於B中不等於A的第一個字母。換成僞代碼就是A[i] == B[j] and A[j] == B[i],i和j是A和B兩個不同字母的索引。

在處理第二種情況時,使用了HashSet,將A變成字符數組存入其中,只要HashSet的size小於A的長度,就說明A中包含重複出現的字母,符合條件。

在處理第三種情況時,使用了兩個List,分別存儲了A中、B中對應索引位置不相等的字母,最後判斷存儲A中字母List的長度、兩個List中的元素是否交叉相等這兩個條件。

此解法的時間複雜度是O(N),空間複雜度是O(N)。

public boolean buddyStrings2(String A, String B) {
    if (A.length() != B.length()) {
        return false;
    }
    if (A.equals(B)) {
        Set<Character> set = new HashSet<Character>();
        for (char ch : A.toCharArray()) {
            set.add(ch);
        }
        return set.size() < A.length();
    } else {
        List<Character> list = new ArrayList<Character>();
        List<Character> list2 = new ArrayList<Character>();
        int n = A.length();
        for (int i=0; i<n; i++) {
            char c = A.charAt(i);
            char c2 = B.charAt(i);
            if (c != c2) {
                list.add(c);
                list2.add(c2);
            }
        }
        return list.size() == 2 && 
            list.get(0) == list2.get(1) && 
            list.get(1) == list2.get(0);
    }
}


04 第二種解法

對於上面的解法,還可以再優化下,在處理第三種情況的時候,只是用一個List,改爲存儲A和B對應位置不相等字母的索引,最後判斷List的長度是否等於2,以及A和B在List中的索引位置上對應的字母是否交叉相等。

此解法的時間複雜度是O(N),空間複雜度是O(N)。

public boolean buddyStrings3(String A, String B) {
    if (A.length() != B.length()) {
        return false;
    }
    if (A.equals(B)) {
        Set<Character> set = new HashSet<Character>();
        for (char ch : A.toCharArray()) {
            set.add(ch);
        }
        return set.size() < A.length();
    } else {
        List<Integer> diffIndex = new ArrayList<Integer>();
        int n = A.length();
        for (int i=0; i<n; i++) {
            if (A.charAt(i) != B.charAt(i)) {
                diffIndex.add(i);
            }
        }
        return diffIndex.size() == 2 && 
            A.charAt(diffIndex.get(0)) == B.charAt(diffIndex.get(1)) &&  
            A.charAt(diffIndex.get(1)) == B.charAt(diffIndex.get(0));
    }
}


05 第三種解法

對於前一種解法,我們還可以再優化下。

在第二種情況中,使用一個長度爲26的整數數組,來判斷A中是否有重複出現的字母。

在第三種情況中,使用兩個臨時變量存儲A和B中對應位置字母不相等的索引值,如果字母不相等的個數多於2個就直接返回false,最後判斷兩個索引位置上對應的字母是否交叉相等。

此解法的時間複雜度是O(N),空間複雜度是O(1)。

public boolean buddyStrings4(String A, String B) {
    if (A.length() != B.length()) {
        return false;
    }
    if (A.equals(B)) {
        int[] arr = new int[26];
        for (char ch : A.toCharArray()) {
            if (++arr[ch-'a'] >= 2) {
                return true;
            }
        }
        return false;
    } else {
        int index = -1;
        int index2 = -1;
        int n = A.length();
        for (int i=0; i<n; i++) {
            if (A.charAt(i) != B.charAt(i)) {
                if (index == -1) {
                    index = i;
                } else if (index2 == -1) {
                    index2 = i;
                } else {
                    return false;
                }
            }
        }
        return A.charAt(index) == B.charAt(index2) &&  
            A.charAt(index2) == B.charAt(index);
    }
}


06 小結

算法專題目前已連續日更超過五個月,算法題文章200+篇,公衆號對話框回覆【數據結構與算法】、【算法】、【數據結構】中的任一關鍵詞,獲取系列文章合集。

以上就是全部內容,如果大家有什麼好的解法思路、建議或者其他問題,可以下方留言交流,點贊、留言、轉發就是對我最大的回報和支持!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章