1. 程式人生 > >LeetCode演算法題-Longest Palindrome(五種解法)

LeetCode演算法題-Longest Palindrome(五種解法)

這是悅樂書的第220次更新,第232篇原創

01 看題和準備

今天介紹的是LeetCode演算法題中Easy級別的第87題(順位題號是409)。給定一個由小寫或大寫字母組成的字串,找到可以用這些字母構建的最長的迴文長度。這是區分大小寫的,例如“Aa”在這裡不被視為迴文。例如:

輸入:“abccccdd”
輸出:7
說明:可以建造的最長的迴文是“dccaccd”,其長度為7。

注意:假設給定字串的長度不超過1,010。

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

02 第一種解法

給定的字元範圍是大小寫字母,因此使用一個長度為256的整型陣列,來儲存字串s中每個字元出現的次數。想要構成迴文,字串中的各個字元出現的次數分為兩種情況:一是該字元出現偶數次,二是該字元出現奇數次。出現偶數次可以直接拿來使用,只要放在對稱位置即可;出現奇數次時,如果是1次,那麼該字元只能當做迴文的中心且只能用一次,如果大於1次,則其偶數次部分可以直接使用,並且多餘的1次可以當做迴文的中心且只能使用一次。

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

public int longestPalindrome(String s) {
    int[] arr = new int[256];
    for (int i=0; i<s.length(); i++) {
        arr[s.charAt(i)]++;
    }
    int count = 0;
    int single = 0;
    for (int j=0; j<arr.length; j++) {
        if (arr[j]%2 == 0) {
            count += arr[j];
        } else {
            count += arr[j]-1;
            single = 1;
        }
    }
    return count+single;
}


03 第二種解法

此解法的思路和第一種解法一致,只是在計數判斷那裡有點區別。對陣列中當前元素直接除以2得到商,再乘以2,得到當前字元可以使用的次數,但是同樣需要判斷奇數次元素,如果當前元素是奇數,並且count是偶數,即count之前沒遇到奇數,那麼count自加1,並且只會執行一次,因為迴文字串中心只能是一次。

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

public int longestPalindrome2(String s) {
    int[] arr = new int[256];
    for (int i=0; i<s.length(); i++) {
        arr[s.charAt(i)]++;
    }
    int count = 0;
    for (int j=0; j<arr.length; j++) {
        count += (arr[j]/2)*2;
        if (arr[j]%2 == 1 && count%2 == 0) {
            count++;
        }
    }
    return count;
}


04 第三種解法

此解法思路和第二種解法一致,有區別的是判斷出現奇數次字元並且count加1的邏輯從迴圈體中抽出來了,放在了最後。如果count和字串s的長度做比較時,相等則說明s中的字元都是出現偶數次,不相等則說明s中有出現奇數次的字元,count就需要加1,且只能加1次。

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

public int longestPalindrome3(String s) {
    int[] arr = new int[256];
    for (int i=0; i<s.length(); i++) {
        arr[s.charAt(i)]++;
    }
    int count = 0;
    for (int j=0; j<arr.length; j++) {
        count += (arr[j]/2)*2;
    }
    return count == s.length() ? count : count+1;
}


05 第四種解法

使用HashSet,如果當前字元已經在set中存在,那麼count加2,並且要移除當前字元;如果不存在,就add進set中。最後判斷set是否為空,如果不為空,則說明有些字元出現了奇數次,就只能當做迴文字元組中心來用,且只能用一次。

此解法的時間複雜度是O(n),雖然使用了contains方法,但是HashSet記憶體儲的資料不會達到O(n)規模,因此遍歷判斷是否包含當前字元需要的最多判斷次數為52次,即O(52*n),也即O(n)。

空間複雜度是O(1),雖然使用了HashSet,但是字元的範圍只是大小寫字母,最大長度是52,可以看做是一個常量。

public int longestPalindrome4(String s) {
    Set<Character> set = new HashSet<Character>();
    int count = 0;
    for (int i=0; i<s.length(); i++) {
        if (set.contains(s.charAt(i))) {
            count = count + 2;
            set.remove(s.charAt(i));
        } else {
            set.add(s.charAt(i));
        }
    }
    if (!set.isEmpty()) {
        count = count + 1;
    }
    return count;
}


06 第五種解法

參照第四種解法的思路,我們同樣可以使用陣列來操作。陣列的長度控制在58,因為a到z是從97到122,A到Z是從65到90,中間相隔58個數。將s變為字元陣列,如果當前字元表示的十進位制數,在陣列中對應的元素為0就自加1,否則就自減1,然後count加2,最後再去判斷陣列中的元素有沒有大於0的存在,如果存在即表示有字元出現了奇數次,然後count需要加1,且只用加一次。

public int longestPalindrome5(String s) {
    int[] arr = new int[58];
    int count = 0;
    for (char ch : s.toCharArray()) {
        if (arr[ch-'A'] == 0) {
            arr[ch-'A']++;
        } else {
            arr[ch-'A']--;
            count += 2;
        }
    }
    for (int j=0; j<arr.length; j++) {
        if (arr[j] > 0) {
            count += 1;
            break;
        }
    }
    return count;
}


07 小結

演算法專題目前已連續日更超過兩個月,演算法題文章87+篇,公眾號對話方塊回覆【資料結構與演算法】、【演算法】、【資料結構】中的任一關鍵詞,獲取系列文章合集。

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