【LeetCode】字符串專題

 

目錄

注意:

38. 外觀數列

49. 字母異位詞分組

151. 翻轉字符串裏的單詞

165. 比較版本號

929. 獨特的電子郵件地址

5. 最長迴文子串

6. Z 字形變換

3. 無重複字符的最長子串

208. 實現 Trie (前綴樹)

273. 整數轉換英文表示

166. 分數到小數

131. 分割回文串

227. 基本計算器 II


注意:

主要和b站大雪菜一起刷題,寶藏up主(https://www.bilibili.com/video/BV1Vt411M741

38. 外觀數列

思路:

  • 這道題是個模擬題
  • 每一輪循環尋找連續字符
  • 注意循環是n-1
class Solution:
    def countAndSay(self, n: int) -> str:
        res = "1"
        for i in range(n-1):
            tmp = ""
            j = 0
            while j<len(res):
                k = j
                while (k<len(res)) and (res[k]==res[j]):
                    k += 1
                tmp += str(k-j)+res[j]
                j = k
            res = tmp
        return res

49. 字母異位詞分組

思路:

  • 字母的異位詞,我們要找到它們的共同點
  • 可以對每個詞進行排序,字母相同的詞排序結果是一樣的,就可以進行分組
  • python對字符串進行sorted之後是個list,不是str類型,需要處理
  • 排序的總時間O(nmlogm+n)n個詞,m爲長度
class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        dic = {}
        for s in strs:
            tmp = s
            tmp = ''.join(sorted(tmp))
            # print(tmp)
            dic[tmp] = dic.get(tmp, []) + [s]
        res = []
        for v in dic.values():
            res.append(v)
        return res

151. 翻轉字符串裏的單詞

思路:

  • 可以每個單詞進行翻轉,再對整個字符串翻轉
  • 這裏用python的strip、split,會更方便一點
class Solution:
    def reverseWords(self, s: str) -> str:
        s = s.strip()
        s = s.split(' ')
        i = 0
        while i<len(s):
            if s[i] == '':
                s.pop(i)
                continue
            i += 1
        return ' '.join(s[::-1])

165. 比較版本號

思路:

  • 將版本號根據'.'分隔開,依次比較,若相等則繼續比較下一段
  • 若不相等直接比較大小返回1或-1
class Solution:
    def compareVersion(self, version1: str, version2: str) -> int:
        i,j=0,0
        while i<len(version1) or j<len(version2):
            x,y = i, j
            while x<len(version1) and version1[x] != '.':
                x += 1
            while y<len(version2) and version2[y] != '.':
                y += 1
            a = 0 if x==i else int(version1[i:x])
            b = 0 if y==j else int(version2[j:y])
            if a<b:
                return -1
            if a>b:
                return 1
            i = x+1
            j = y+1
        return 0

929. 獨特的電子郵件地址

思路:

  • 用find函數,find(str,begin,end)找到‘@’的下標
  • 將email地址分割成name+domain,然後對name進行單獨的處理,再與‘@’和domain組合獲得新的郵件地址,得到最後的答案
  • 用set去重存儲所有的答案,返回set的長度即可
class Solution:
    def numUniqueEmails(self, emails: List[str]) -> int:
        res = set()
        for email in emails:
            at = email.find('@')
            name = ""
            for c in email[:at]:
                if c == '+':
                    break
                elif c != '.':
                    name += c
            domain = email[at+1:]
            s = name+"@"+domain
            res.add(s)
        return len(res)

5. 最長迴文子串

思路:

  • 枚舉每一個可能的中心點,分別用j,k兩個指針向兩邊擴展,所指指針相等則繼續移動,不相等則跳出判斷是不是最長的
  • 中心點可能是兩個(迴文子串長度爲偶數),也可能是一個(迴文子串長度爲奇數)
  • 時間複雜度爲O(n^2),n爲s的長度
class Solution:
    def longestPalindrome(self, s: str) -> str:
        max_res = ""
        for i in range(len(s)):
            j,k=i,i
            # 迴文串長度爲奇數
            while j>=0 and k<len(s) and s[j] == s[k]:
                j -= 1
                k += 1
            tmp = s[j+1:k]
            if len(tmp) > len(max_res):
                max_res = tmp
            j,k=i,i+1
            # 迴文串長度爲偶數
            while j>=0 and k<len(s) and s[j] == s[k]:
                j -= 1
                k += 1
            tmp = s[j+1:k]
            if len(tmp) > len(max_res):
                max_res = tmp
        return max_res

6. Z 字形變換

思路:

  • 這題主要是找規律,第一排和最後一排都是首項爲i,公差爲2*(numrows-1)
  • 中間的部分是兩個等差數列交叉出現
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows==1:
            return s
        res = ""
        for i in range(numRows):
            # 第一行或者最後一行
            if i==0 or i==numRows-1:
                j = i
                while j<len(s):
                    res += s[j]
                    j += 2*(numRows-1)
            else:# 其餘行
                j,k=i,2*(numRows-1)-i
                while j<len(s) or k<len(s):
                    res += s[j] if j<len(s) else ""
                    res += s[k] if k<len(s) else ""
                    j += 2*(numRows-1)
                    k += 2*(numRows-1)
        return res

3. 無重複字符的最長子串

思路:

  • 利用雙指針,i代表當前無重複字符子串的頭,j代表當前無重複字符子串的尾
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        if not s:
            return 0
        i = 0
        max_len = 1
        for j in range(1,len(s)):
            while s[j] in s[i:j]:
                i += 1
            if j-i+1>max_len:
                max_len = j-i+1
        return max_len

208. 實現 Trie (前綴樹)

思路:

  • 前綴樹的結構就是,根節點爲空,每一個節點有26個子節點,出現那個字符就將對應位置的指針賦予它
  • 每一個節點還要標記下當前節點是不是某個單詞的結尾
class Node:
    def __init__(self):
        self.is_end = False
        self.son = [None]*26
 
class Trie:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.root = Node()

    def insert(self, word: str) -> None:
        """
        Inserts a word into the trie.
        """
        p = self.root
        for ch in word:
            index = ord(ch) - ord('a')
            if not p.son[index]:
                p.son[index] = Node()
            p = p.son[index]
        p.is_end = True


    def search(self, word: str) -> bool:
        """
        Returns if the word is in the trie.
        """
        p = self.root
        for ch in word:
            index = ord(ch) - ord('a')
            if not p.son[index]:
                return False
            p = p.son[index]
        return p.is_end


    def startsWith(self, prefix: str) -> bool:
        """
        Returns if there is any word in the trie that starts with the given prefix.
        """
        p = self.root
        for ch in prefix:
            index = ord(ch) - ord('a')
            if not p.son[index]:
                return False
            p = p.son[index]
        return True



# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

273. 整數轉換英文表示

思路:

  • 這題主要是比較麻煩,但是想清楚就會好一點。
  • 英文中是三個數字三個數字爲一部分,__billion__million__thousand__,其中__爲1~999,最後一個爲0~999
  • 對於1000以內的數寫一個表達函數,然後套入上面這個形式就可以
class Solution:
    def __init__(self):
        self.small = ["Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven"
                      "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen",
                      "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"]
        self.decade = ["", "", "Twenty", "Thirty", "Fourty", "Fifty", "Sixty", "Seventy",
                       "Eighty", "Ninety"]
        self.big = ["", "Thousand", "Million", "Billion"]

    def numberToWords(self, num: int) -> str:
        if num == 0:
            return self.small[0]
        i,j = 1000000000, 3
        res = ""
        while i and j>=0:
            if num>i:
                res += self.get_part(num//i) + self.big[j]
            i //= 1000
            j -= 1
        res = res.strip()
        return res

    # 1000以內表達形式
    def get_part(self, num):
        res = ""
        if num > 100:
            res += self.small[num//100] + " Hundred "
            num %= 100
        if not num:
            return res
        if num > 20:
            res += self.decade[num//10] + ' '
            num %= 10
        if not num:
            return res
        else:
            res += self.small[num] + ' '
        return res

166. 分數到小數

思路:

  • 又是一道模擬題,要把步驟捋清楚
  • 首先判斷分子分母是不是負數,是的話需要進行處理成正數,同時記錄下結果是否要添加負號
  • 然後來計算,首先整除了沒有餘數了就可以直接輸出答案
  • 若是餘數不爲0說明還有小數部分,小數部分的計算就和手算步驟一樣,關鍵是如何判斷是循環節
  • 用一個dic哈希表儲存當前數字的位置,若有一個數字之前存在過,則後面的餘數會是一樣的,就可以得出循環節
class Solution:
    def fractionToDecimal(self, numerator: int, denominator: int) -> str:
        minus = False
        if numerator<0:
            minus = not minus
            numerator = -numerator
        if denominator<0:
            minus = not minus
            denominator = -denominator
        res = str(numerator//denominator)
        numerator = numerator%denominator
        # 整除
        if not numerator:
            if minus and res!="0":
                res = "-"+res
            return res
        # 計算小數部分
        res += "."
        dic = {}
        while numerator:
            if dic.get(numerator,0):
                res = res[:dic[numerator]] + "(" +res[dic[numerator]:] + ")"
                break
            else:
                dic[numerator] = len(res)
            numerator *= 10
            res += str(numerator//denominator)
            numerator %= denominator
        if minus:
            res = "-"+res
        return res

131. 分割回文串

思路:

  • l這道題利用dfs+回溯遍歷所有方案即可
  • 寫個判斷迴文串的函數
class Solution:
    def partition(self, s: str) -> List[List[str]]:
        res = []
        self.dfs("", 0, s, res, [])
        return res
    
    def dfs(self, now, cur, s, ans, path):
        if cur == len(s):
            if self.check(now):
                path.append(now)
                ans.append(path[:])
                path.pop()
            return
        if self.check(now):
            path.append(now)
            self.dfs("", cur, s, ans, path)
            path.pop()
        self.dfs(now+s[cur], cur+1, s, ans, path)

    
    def check(self, s):
        if not s:
            return False
        i,j=0, len(s)-1
        while i<j:
            if s[i]!=s[j]:
                return False
            i += 1
            j -= 1
        return True

227. 基本計算器 II

思路:

  • 又是一道想起來比較複雜的題,由於有加減乘除的符號,考慮符號的優先級,可以用兩個棧,一個存儲符號,一個存儲數字
  • 若是數字就要進行計算,查看棧頂,是乘除符號則直接計算,若是加減符號則判斷下棧裏的加減符號有沒有超過兩個,(a+b+c*d)這裏可以發現a+b可以先計算的。
  • 在原本數字的末尾添加+0方便棧最後進行計算
class Solution:
    def calculate(self, s: str) -> int:
        op, num = [], []
        s += "+0"
        i = 0
        while i<len(s):
            # 空格跳過
            if s[i] == ' ':
                i += 1
                continue
            # 符號
            if s[i] == '+' or s[i] == '-' or s[i] == '*' or s[i] == '/':
                op.append(s[i])
            else:
                # 獲取數字
                j = i
                while j<len(s) and ('0'<=s[j]<='9'):j+=1
                num.append(int(s[i:j]))
                i = j-1
                # 操作符棧非空
                if op:
                    if op[-1]=='*' or op[-1]=='/':
                        x = num.pop()
                        y = num.pop()
                        if op[-1]=='*':
                            num.append(x*y)
                        else:
                            num.append(y//x)
                        op.pop()
                    elif len(op)>=2:
                        c = num.pop()
                        b = num.pop()
                        a = num.pop()
                        op2 = op.pop()
                        op1 = op.pop()
                        if op1 == '+':
                            num.append(a+b)
                        else:
                            num.append(a-b)
                        num.append(c)
                        op.append(op2)
            i += 1
        num.pop()
        # print(num)
        return num[-1]

 

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