【數組和字符串】(三) 字符串簡介

目錄

三、字符串簡介

3.1 最長公共前綴

3.2 最長迴文子串

3.3 翻轉字符串裏的單詞

3.4 實現 strStr (選修字符串匹配算法:KMP) (未完)


三、字符串簡介

3.1 最長公共前綴

3.1.1 問題描述

3.1.2 求解過程

法一:注意是“公共”前綴,而 不是“出現次數最多的”前綴,因此,每次只需要判斷前綴切片出現次數是否等於列表中字符串總數即可。一旦前綴切片出現頻次小於字符串總數,則不滿足公共,停止尋找。(審題很重要!)

2020/06/04 - 24.13% - 使用了太多技巧,複雜度太高!效率很低!且冗餘計算很多!

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        num = len(strs)  # 字符串總數
        if num == 0:  # 特殊情況 []
            return ""
        if num == 1:  # 特殊情況 [""] ["ab"]
            return strs[0]
        f = ""  # 初始化輸出前綴字符串
        max_len = len(max(strs, key=lambda x: len(x)))  # 最長字符串長度
        for i in range(1, max_len+1):  # 使用切片不會有 index out of range 的問題
            f_temp, n_temp = collections.Counter(map(lambda y: y[:i], strs)).most_common()[0]       
            if n_temp == num:  # 如果該前綴出現頻次=字符串總數
                f = f_temp     # 將其作爲輸出前綴字符串
            else:
                return f
        return f

法二:注意到,作爲“公共”前綴,事實上根本不需要那麼多技巧和遍歷。只需要選其中一個單詞切片,遍歷數組依次對比以確定當前切片是否是“公共”的。一旦發現非公共,直接返回當前切片。否則,擴大切片範圍再遍歷一次。

2020/06/04 - 77.66% - 思路有對,可以繼續優化!

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        if len(strs) == 0:
            return ""
        
        result = ""  # 初始化輸出結果
        first_str = strs[0]  # 首單詞
        for i in range(1, len(first_str)+1):  # 根據參考答案, 使用 find 可以簡化 for 後操作
            cur_front = first_str[:i]  # 當前前綴

            #for j in range(1, length):  # 用單純的 for 比 enumerate 慢 20%
            #    if strs[j][:i] != cur_front:

            for _, cur_str in enumerate(strs):
                if cur_str[:i] != front:  # 但凡發現一個不一樣, 直接 return
                    return result
            result = cur_front  # 遍歷數組, 確定公共前綴
        return result

法三參考寫法,太強了,充分利用 Python 特性:zip() 拉鍊函數 + * 打包 + 集合 set 等實現最優化,化繁爲簡,佩服!經過觀察,最快的方法 無一不是使用 zip() 函數

2020/06/04 - 99.99% - 最快!

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        res = ""
        # 通過 zip 縫合所有元素, 超過最短的部分會被截斷
        for i in zip(*strs): 
            #print(i)
            if len(set(i)) == 1:  # 使用集合判斷當前位置字符是否公共
                res += i[0]
            else:
                break 
        return res

3.2 最長迴文子串

3.2.1 問題描述

3.2.2 求解過程

法一:樸素法,從寬到窄的滑動窗口,依次對其中的字符串切片進行正、逆序比較。一旦符合比較,就是當前最長的迴文字符串了,直接返回之。否則,找到最後也沒有,就隨意返回一個字符。複雜度 O(n^3)。

2020/06/04 - 36.83% - 看來不怎麼樣!(4752 ms太低效了)

class Solution:
    def longestPalindrome(self, s: str) -> str:
        length = len(s)
        if length <= 1:  # 特殊情況如 "" "a"
            return s
        
        times = 1  # 首次用全長窗口, 只需要比較1次
        for width in range(length, 0, -1):  # 滑動窗口寬度-1
            for start in range(times):  # 滑動窗口移動次數 times
                window = s[start:start+width]  # 滑動窗口內字符串切片
                if window == window[::-1]:  # 正序逆序比較
                    return window
            times += 1  # 滑動窗口寬度-1時,移動次數+1
        return s[0]  # 如果一直找不到就隨意返回一個字符         

法二參考答案原理待回顧

2020/06/04 - 99.94% - 最快!(52 ms)

class Solution:
    def longestPalindrome(self, s: str) -> str:
        length = len(s)
        if length < 2 or s == s[::-1]:
            return s
        else:
            max_length = 1 
            start_pointer = 0 
            for i in range(1, length):
                even = s[i-max_length: i+1]   # 切片
                odd = s[i-max_length-1: i+1]  # 切片
                if (i - max_length-1 >= 0) and (odd == odd[::-1]):
                    start_pointer = i - max_length - 1 
                    max_length += 2
                    continue
                if (i - max_length >= 0) and (even == even[::-1]):
                    start_pointer = i - max_length
                    max_length += 1
            return s[start_pointer: start_pointer+max_length] 

3.3 翻轉字符串裏的單詞

3.3.1 問題描述

 

3.3.2 求解過程

法一:樸素法,使用 split(" ") 將原字符串以 " " 爲間隔劃分爲字符串列表,使用 [::-1] 反轉字符串列表。注意,此時字符串列表仍包含有空字符串。爲此,通過 for 循環篩選出非空字符串並加入結果字符串 res 中,然後加入字符串間隔空格。最後,使用 str.rstrip() 刪除末端空格。複雜度 O(n)。

2020/06/06 - 90.57% - 樸素的思路

class Solution:
    def reverseWords(self, s: str) -> str:
        res = ""
        tmp = s.split(" ")[::-1]
        for word in tmp:
            if word:
                res += word
                res += " "
        return res.rstrip()

法二:組合 Python 字符串方法 split() 和 join() 實現拆分和重組,通過 list[::-1] 反轉中間形式的字符串列表。複雜度 O(n)。

2020/06/06 - 99.99% - 雖然最快,但是太依賴內建方法。

class Solution:
    def reverseWords(self, s: str) -> str:
        if not s:
            return ""
        word_list = s.split()[::-1]  # 獲取所有有效的字符串列表並反轉
        strs = " ".join(word_list)   # 將列表元素以" "爲間隔合成新字符串
        return strs
        # return " ".join(s.split()[::-1])  # 以上可一言以蔽之 

3.4 實現 strStr (選修字符串匹配算法:KMP)

3.4.1 問題描述

3.4.2 求解過程

法一:(基準) 直接使用 Python 字符串內置方法 str.find() 實現查找匹配。如果這樣寫,面試官肯定要把你踢出去的......

2020/06/07 - 60.60% - 看來還不是最快的!

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        return haystack.find(needle)

 法二:(暴力匹配改) 不同於每次都在 haystack 上滑動窗口取出切片來與 needle 比較的暴力匹配方式,本方法先試探性比較首字母,倘若相同纔有興趣比較全部,從而節省了大量時間。複雜度 O(n) 嗎?(怎麼算啊?)

202/06/08 - 97.06% - 看來還行

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        if not needle:  # len(needle) == 0 返回 0
            return 0
        
        lenh = len(haystack)
        lenn = len(needle)
        if lenh < lenn:  # 如果長度不夠肯定不行返回 -1
            return -1
        
        for i in range(lenh-lenn+1):  # 遍歷
            if haystack[i] == needle[0]:  # 只有首字母相同纔有興趣比較其餘部分
                if haystack[i:i+lenn] == needle:
                    return i   
        return -1

法三:(KMP 算法):待續。。。

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