1. 程式人生 > >LeetCode-Algorithms #005 Longest Palindromic Substring, Database #179 Consecutive Numbers

LeetCode-Algorithms #005 Longest Palindromic Substring, Database #179 Consecutive Numbers

weight 成績 通過 傳說 while lse 做到 tinc 特殊字符

LeetCode-Algorithms #005 Longest Palindromic Substring

技術分享圖片

英語學習時間
palindromic: [醫] 復發的, 再發的

在數學和計算機上,就指回文

這道題目就是找出給定字符串中最長的回文子串, 可以假定原字符串的長度不超過1000

直接遍歷來做肯定是不難, 但也可以想見一定是慢得可以.

那麽我的另一個想法是, 從原串中的每一個字符, 或每兩個字符中間的空隙開始, 向左右兩邊判斷是否為回文串, 最後找出最長的

 1 class Solution {
 2     public String longestPalindrome(String s) {
3 //首先排除特殊情況 4 if (s == null || "".equals(s) || s.length() == 1) { 5 return s; 6 } else { 7 //先建立一個空串以儲存結果 8 String result = ""; 9 //獲取原字符串的長度 10 int len = s.length(); 11 12 //因為之前已經排除了特殊情況, 所以到這裏原串的長度至少為2, 那麽為了找到最長的回文子串, 我們只需要遍歷到倒數第二個字符即可
13 for (int i = 0; i < len - 1; i++) { 14 //首先考慮以第i個字符為中心, 向兩側做判斷的情況 15 int begin = i, end = i; 16 while (true) { 17 //如果begin或end的外側未達到邊界則繼續判斷 18 if (begin > 0 && end < len - 1) { 19 //
如果begin左側的字符和end右側的字符相同則向兩側延伸一位 20 if (s.charAt(begin - 1) == s.charAt(end + 1)) { 21 begin--; 22 end++; 23 } else { //反之則結束循環 24 break; 25 } 26 } else { //如果begin或end已達到邊界則終止循環 27 break; 28 } 29 } 30 //判斷最後取得的回文子串是否比之前存儲的結果長 31 if (result.length() < (end - begin + 1)) { 32 //如果是則替換結果 33 result = s.substring(begin, end + 1); 34 } 35 36 //再以i和i+1之間的空隙為中心向兩側進行判斷 37 begin = i; 38 end = i + 1; 39 //如果i處的字符和i+1處不同則直接跳過, 反之則繼續進行判斷 40 if (s.charAt(begin) == s.charAt(end)) { 41 //後面內容同上半部分 42 while (true) { 43 if (begin > 0 && end < len - 1) { 44 if (s.charAt(begin - 1) == s.charAt(end + 1)) { 45 begin--; 46 end++; 47 } else { 48 break; 49 } 50 } else { 51 break; 52 } 53 } 54 if (result.length() < (end - begin + 1)) { 55 result = s.substring(begin, end + 1); 56 } 57 } 58 } 59 //最後返回結果 60 return result; 61 } 62 } 63 }

技術分享圖片

順利通過, 但是成績很一般, 作為一道經典題目, 強者們的速度可以說相當喪心病狂了,

這裏面涉及到一個經典算法--Manacher‘s Algorithm, 也就是傳說中的馬拉車算法, 今天研究了一下, 可以稍微談一談,

我上面的這種寫法, 時間復雜度是O(n2), 沒有進行任何的優化, 如果跳過一些不必要的計算, 應該還可以快上不少, 但馬拉車可以做到O(n)的時間復雜度, 相當高端了

具體實現的思路是這樣的:

在我上面的寫法中, 從字符串的第一個字符開始進行遍歷時, 每次檢驗都要分兩種情況:

1) 以該字符為中心向兩側檢驗

2) 以該字符與該字符的下一個字符間的空隙為中心向兩側檢驗

而在馬拉車算法中, 會首先向字符串的各個字符間插入一個特殊字符(多用#)

這樣 babad 就會變成 #b#a#b#a#d#, aa 就會變成 #a#a#, 這樣就避免了分類討論的情形.

之後, 我們建立一個與新字符串長度相同的整數數組, 每個位置存儲以該位置為中心的回文子串的長度,如:

#b#a#b#a#d#
13171713131

為了方便計算, 我們再將這個結果長度都除以二:

#b#a#b#a#d#
01030301010

如果有了這個數組, 那麽最長的回文子串顯然也就找到了, 我們的目標就是計算出這個數組, 讓我們看一下馬拉車的思路

#a#b#a#b#a#b#a#b#a#c#d#e#

如對上面這個數組進行遍歷,

第一位#對應的顯然是0, 第二位a對應的是1, 第三位#是0, 第四位b是3,第五位#是0,第六位a是5,第7位#是0,

對第8位的b進行判斷時, 由於以第6位a為中心的回文子串會向左右各覆蓋5位, 那麽顯然第8位的b在這個範圍之內.

那麽, 以第8位b為中心的回文子串最少應該與以第四位b為中心的回文子串等長, 也就是左右各覆蓋三位,

因此我們可以從第8位b的左右各第四位開始測試, 這樣就節約了計算量, 最後得到第8位b對應的數值是7.

按照這個思路, 我們可以在遍歷中設定一個最右邊界, 在對特定字符進行檢驗是, 如果該字符在已知的回文子串覆蓋下,

那麽就計算其相對最右邊界回文串中心對稱的位置, 得出已知回文串的長度.

判斷該長度和右邊界,如果達到了右邊界,那麽需要進行中心擴展探索。

當然,如果第3步該字符沒有在最右邊界的“羽翼”下,則直接進行中心擴展探索。進行中心擴展探索的時候,同時又更新右邊界.

上面的敘述部分參考(抄襲)了下面這篇文章.

【面試現場】如何找到字符串中的最長回文子串?

這裏貼一個寫得很漂亮的答案, 雖然用的是馬拉車的思路, 但並不完全按照上面的步驟實現

 1 public class Solution {
 2     
 3     int len = 0, maxLength = 0, init = 0;
 4     
 5     public String longestPalindrome(String s) {
 6         
 7         char[] chars = s.toCharArray();
 8         
 9         len = s.length();
10         
11         if (len <= 1) return s;
12         
13         for (int i = 0; i < len; i++) {
14             i = manacher(chars, i);
15         }
16         return s.substring(init, init + maxLength);
17     }
18     
19     public int manacher(char[] chars, int k) {
20         
21         int i = k - 1, j = k;
22         while (j < len - 1 && chars[j] == chars[j + 1]) j++;
23         int nextCenter = j++;
24         
25         while (i >= 0 && j < len && chars[i] == chars[j]) {
26             i--;
27             j++;
28         }
29         
30         if (j - i - 1 > maxLength) {
31             maxLength = j - i - 1;
32             init = i + 1;
33         }
34         
35         return nextCenter;
36     }
37     
38 }

LeetCode-Database #179 Consecutive Numbers

技術分享圖片

找出所有連續出現過至少三次的數字

 1 SELECT DISTINCT
 2     l1.Num AS ConsecutiveNums
 3 FROM
 4     Logs l1,
 5     Logs l2,
 6     Logs l3
 7 WHERE
 8     l1.Id = l2.Id - 1
 9     AND l2.Id = l3.Id - 1
10     AND l1.Num = l2.Num
11     AND l2.Num = l3.Num
12 ;

答案長這樣, 也沒什麽好說的了

再貼一個好一點的答案

 1 # Write your MySQL query statement below
 2 # select a.Num as ConsecutiveNums from Logs a inner join 
 3 # (select b.Id, b.Num from Logs b inner join Logs c
 4 # on b.Id=c.Id-1 and b.Num=c.Num) d
 5 # on a.Id=d.Id-1 and a.Num=d.Num
 6 # Group by a.Num
 7 
 8 SELECT DISTINCT(Num) AS ConsecutiveNums
 9 FROM (
10     SELECT
11     Num,
12     @counter := IF(@prev = Num, @counter + 1, 1) AS how_many_cnt_in_a_row,
13     @prev := Num
14     FROM Logs y, (SELECT @counter:=1, @prev:=NULL) vars
15 ) sq
16 WHERE how_many_cnt_in_a_row >= 3

LeetCode-Algorithms #005 Longest Palindromic Substring, Database #179 Consecutive Numbers