【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]

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值