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+篇,公眾號對話方塊回覆【資料結構與演算法】、【演算法】、【資料結構】中的任一關鍵詞,獲取系列文章合集。
以上就是全部內容,如果大家有什麼好的解法思路、建議或者其他問題,可以下方留言交流,點贊、留言、轉發就是對我最大的回報和支援!