【Hard——Day8】65.有效数字、68.文本左右对齐、76.最小覆盖子串

题目链接

https://leetcode.cn/problems/valid-number/description/

https://leetcode.cn/problems/text-justification/description/

https://leetcode.cn/problems/minimum-window-substring/description/

题解链接

65.有效数字

https://leetcode.cn/problems/valid-number/solutions/2361385/65-you-xiao-shu-zi-you-xian-zhuang-tai-z-5lde

https://leetcode.cn/problems/valid-number/solutions/251105/python3jue-dui-jian-ji-yi-dong-de-jie-fa-by-cheng-

https://leetcode.cn/problems/valid-number/solutions/1065899/bao-zheng-yi-fen-zhong-kan-dong-fei-chan-t4fl

68.文本左右对齐

https://leetcode.cn/problems/text-justification/solutions/259316/python334xing-dai-ma-bao-zheng-jian-dan-yi-dong-by

https://leetcode.cn/problems/text-justification/solutions/6314/shun-zhao-si-lu-xiang-by-powcai

https://leetcode.cn/problems/text-justification/solutions/535122/golang-jian-ji-xie-fa-by-endlesscheng-180a

76.最小覆盖子串

https://leetcode.cn/problems/minimum-window-substring/solutions/1503454/by-flix-1kac

https://leetcode.cn/problems/minimum-window-substring/solutions/3618394/hua-dong-chuang-kou-ha-xi-biao-pythonjav-f4km

https://leetcode.cn/problems/minimum-window-substring/solutions/2713911/liang-chong-fang-fa-cong-o52mn-dao-omnfu-3ezz

题解

65.有效数字

初见感觉这题是个模拟题。

解法一:字符串

有限状态自动机,第一次看到这种解法。

解法二:字符串

逻辑很清晰,就是有些条件我没看懂。

解法三:字符串、模拟

要看一遍题解说明可能才看得懂代码

68.文本左右对齐

解法一:贪心、字符串

看懂了,但是自己实现起来肯定不会。

解法二:脑筋急转弯

和上一个解法有点像。

解法三:数学

和解法二也有点像。

76.最小覆盖子串

解法一:哈希表、双指针、字符串、滑动窗口

应该多了解一些python的库的API的作用,善于利用。

解法二:哈希表、字符串、滑动窗口

解法三:滑动窗口

方法一

优化法

代码

python 复制代码
#65.有效数字
#解法一:字符串
class Solution:
    def isNumber(self, s: str) -> bool:
        states = [
            { ' ': 0, 's': 1, 'd': 2, '.': 4 }, # 0. start with 'blank'
            { 'd': 2, '.': 4 } ,                # 1. 'sign' before 'e'
            { 'd': 2, '.': 3, 'e': 5, ' ': 8 }, # 2. 'digit' before 'dot'
            { 'd': 3, 'e': 5, ' ': 8 },         # 3. 'digit' after 'dot'
            { 'd': 3 },                         # 4. 'digit' after 'dot' ('blank' before 'dot')
            { 's': 6, 'd': 7 },                 # 5. 'e'
            { 'd': 7 },                         # 6. 'sign' after 'e'
            { 'd': 7, ' ': 8 },                 # 7. 'digit' after 'e'
            { ' ': 8 }                          # 8. end with 'blank'
        ]
        p = 0                           # start with state 0
        for c in s:
            if '0' <= c <= '9': t = 'd' # digit
            elif c in "+-": t = 's'     # sign
            elif c in "eE": t = 'e'     # e or E
            elif c in ". ": t = c       # dot, blank
            else: t = '?'               # unknown
            if t not in states[p]: return False
            p = states[p][t]
        return p in (2, 3, 7, 8)

#解法二:字符串
class Solution:
    def isNumber(self, s: str) -> bool:
        s = s.strip()
        numbers = [str(i) for i in range(10)]
        n = len(s)
        
        e_show_up, dot_show_up, num_show_up, num_after_e = False, False, False, False
        
        for i in range(n):
            c = s[i]
            if c in numbers:
                num_show_up = True
                num_after_e = True
            elif c in ('+', '-'):
                if i > 0 and s[i-1] != 'e':
                    return False
            elif c == '.':
                if dot_show_up or e_show_up:
                    return False
                dot_show_up = True
            elif c == 'e':
                if e_show_up or not num_show_up:
                    return False
                e_show_up = True
                num_show_up = False
            else:
                return False
            
        return num_show_up and num_after_e
     
#解法三:字符串、模拟
def isNumber(self, s: str) -> bool:
    num_dot = s.count('.')
    num_e = s.count('e')
    num_E = s.count('E')
    if num_dot > 1 or num_e > 1 or num_E > 1: return False
    if 'e' not in s and 'E' not in s:
        return self.is_decimal(s) if num_dot == 1 else self.is_integer(s)
    if 'e' in s:
        before_e, after_e = s.split('e')
    elif 'E' in s:
        before_e, after_e = s.split('E')
    return (self.is_decimal(before_e) or self.is_integer(before_e)) and self.is_integer(after_e)

def is_decimal(self, s):
    if len(s) == 0: return False
    if s[0] in ('+', '-'): s = s[1:]
    count_digit = 0
    count_dot = 0
    for char in s:
        if char.isdigit(): count_digit += 1
        elif char == '.': count_dot += 1
        else: return False
    return count_digit >= 1 and count_dot == 1

def is_integer(self, s):
    if len(s) == 0: return False
    if s[0] in ('+', '-'): s = s[1:]
    if len(s) == 0: return False
    for char in s:
        if not char.isdigit(): return False
    return True
python 复制代码
#68.文本左右对齐
#解法一:贪心、字符串
class Solution:
    
    def process(self, curLen, curWords, maxWidth):
        # 空格数量
        num_space = maxWidth - curLen
        # 如果只有一个单词就没必要考虑分配,直接填充空格即可
        if len(curWords) == 1:
            return curWords[0] + ' ' * (maxWidth - curLen)
        # 每个空隙分到的空格数量
        num_sep = num_space // (len(curWords) - 1)
        # 分到空格数量多一个的空隙
        head_sep = num_space % (len(curWords) - 1)
        cur = ''
        # 分配
        for i in range(len(curWords) - 1):
            cur = cur + curWords[i] + (' ' * (num_sep + 1) if i < head_sep else ' ' * num_sep)
        # 分配结束之后把最后一个单词连上
        cur = cur + curWords[-1]
        return cur
        
    def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
        ret = []
        curLen, curWords = 0, []
        
        for w in words:
            # 切分判断的条件,单词长度加上基本的空格长度
            if curLen + len(w) + len(curWords) <= maxWidth:
                curLen += len(w)
                curWords.append(w)
            else:
                ret.append(self.process(curLen, curWords, maxWidth))
                curLen, curWords = len(w), [w]
                
        # 剩下没有安排的就是最后一行,按照最后一行靠左处理
        if len(curWords) > 0:
            cur = ''
            for i in range(len(curWords) - 1):
                cur = cur + curWords[i] + ' '
            cur = cur + curWords[-1]
            cur += ' ' * (maxWidth - len(cur))
            ret.append(cur)
        return ret

#解法二:脑筋急转弯
class Solution:
    def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
        res = []
        n = len(words)
        i = 0

        def one_row_words(i):
            # 至少 一行能放下一个单词, cur_row_len
            left = i
            cur_row_len = len(words[i])
            i += 1
            while i < n:
                # 当目前行 加上一个单词长度 再加一个空格
                if cur_row_len + len(words[i]) + 1 > maxWidth:
                    break
                cur_row_len += len(words[i]) + 1
                i += 1
            return left, i

        while i < n:
            left, i = one_row_words(i)
            # 该行几个单词获取
            one_row = words[left:i]
            # 如果是最后一行了
            if i == n :
                res.append(" ".join(one_row).ljust(maxWidth," "))
                break
            # 所有单词的长度
            all_word_len = sum(len(i) for i in one_row)
            # 至少空格个数
            space_num = i - left - 1
            # 可以为空格的位置
            remain_space = maxWidth - all_word_len
            # 单词之间至少几个空格,还剩几个空格没用
            if space_num:
                a, b = divmod(remain_space, space_num)
            # print(a,b)
            # 该行字符串拼接
            tmp = ""
            for word in one_row:
                if tmp:
                    tmp += " " * a
                    if b:
                        tmp += " "
                        b -= 1
                tmp += word
            #print(tmp, len(tmp))
            res.append(tmp.ljust(maxWidth, " "))
        return res

#解法三:数学
class Solution:
    def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
        ans = []
        n = len(words)
        i = 0
        while i < n:
            start = i  # 这一行第一个单词的下标
            sum_len = -1  # 第一个单词之前没有空格
            while i < n and sum_len + len(words[i]) + 1 <= maxWidth:
                sum_len += len(words[i]) + 1  # 单词之间至少要有一个空格
                i += 1

            extra_spaces = maxWidth - sum_len  # 这一行剩余未分配的空格个数
            gaps = i - start - 1  # 这一行单词之间的空隙个数(单词个数减一)

            # 特殊情况:如果只有一个单词,或者是最后一行,那么左对齐,末尾补空格
            if gaps == 0 or i == n:
                row = ' '.join(words[start: i]) + ' ' * extra_spaces  # 末尾补空格
                ans.append(row)
                continue

            # 一般情况:把 extra_spaces 个空格均匀分配到 gaps 个空隙中(靠左的空格更多)
            avg, rem = divmod(extra_spaces, gaps)
            spaces = ' ' * (avg + 1)  # +1 表示加上单词之间已有的一个空格
            # 前 rem 个空隙多一个空格
            row = (spaces + ' ').join(words[start: start + rem + 1]) + \
                  spaces + spaces.join(words[start + rem + 1: i])
            ans.append(row)
        return ans
python 复制代码
#76.最小覆盖子串
#解法一:哈希表、双指针、字符串、滑动窗口
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        
        if len(t) > len(s):
            return ''        
        
        cnt = collections.Counter(t)    # 哈希表:记录需要匹配到的各个元素的数目
        need = len(t)                   # 记录需要匹配到的字符总数【need=0表示匹配到了】
        
        n = len(s)
        start, end = 0, -1          # 记录目标子串s[start, end]的起始和结尾
        min_len = n + 1             # 符合题意的最短子串长度【初始化为一个不可能的较大值】
        left = right = 0            # 滑动窗口的左右边界
        
        for right in range(n):
            
            # 窗口右边界右移一位
            ch = s[right]               # 窗口中新加入的字符
            if ch in cnt:               # 新加入的字符位于t中
                if cnt[ch] > 0:         # 对当前字符ch还有需求
                    need -= 1           # 此时新加入窗口中的ch对need有影响
                cnt[ch] -= 1
            
            # 窗口左边界持续右移
            while need == 0:            # need=0,当前窗口完全覆盖了t
                if right - left + 1 < min_len:      # 出现了更短的子串
                    min_len = right - left + 1
                    start, end = left, right
                
                ch = s[left]            # 窗口中要滑出的字符
                if ch in cnt:           # 刚滑出的字符位于t中
                    if cnt[ch] >= 0:    # 对当前字符ch还有需求,或刚好无需求(其实此时只有=0的情况)
                        need += 1       # 此时滑出窗口的ch会对need有影响
                    cnt[ch] += 1
                left += 1               # 窗口左边界+1
        
        return s[start: end+1]

#解法二:哈希表、字符串、滑动窗口
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        cnt = Counter(t)
        ans_l = -1
        ans_r = len(s)
        l = 0
        count = len(cnt)
        for r, c in enumerate(s):
            if c in cnt:
                cnt[c] -= 1
                if cnt[c] == 0:
                    count -= 1
            while count == 0:
                if ans_r - ans_l > r - l:
                    ans_l, ans_r = l, r 
                if s[l] in cnt:
                    if cnt[s[l]] == 0:
                        count += 1
                    cnt[s[l]] += 1
                l += 1
        return "" if ans_l == -1 else s[ans_l: ans_r+1]

#解法三:滑动窗口
#方法一
# 请选择 Python3 提交代码,而不是 Python
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        cnt_s = Counter()  # s 子串字母的出现次数
        cnt_t = Counter(t)  # t 中字母的出现次数

        ans_left, ans_right = -1, len(s)
        left = 0
        for right, c in enumerate(s):  # 移动子串右端点
            cnt_s[c] += 1  # 右端点字母移入子串
            while cnt_s >= cnt_t:  # 涵盖
                if right - left < ans_right - ans_left:  # 找到更短的子串
                    ans_left, ans_right = left, right  # 记录此时的左右端点
                cnt_s[s[left]] -= 1  # 左端点字母移出子串
                left += 1
        return "" if ans_left < 0 else s[ans_left: ans_right + 1]
#优化法
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        cnt = defaultdict(int)  # 比 Counter 更快
        for c in t:
            cnt[c] += 1
        less = len(cnt)  # 有 less 种字母的出现次数 < t 中的字母出现次数

        ans_left, ans_right = -1, len(s)
        left = 0
        for right, c in enumerate(s):  # 移动子串右端点
            cnt[c] -= 1  # 右端点字母移入子串
            if cnt[c] == 0:
                # 原来窗口内 c 的出现次数比 t 的少,现在一样多
                less -= 1
            while less == 0:  # 涵盖:所有字母的出现次数都是 >=
                if right - left < ans_right - ans_left:  # 找到更短的子串
                    ans_left, ans_right = left, right  # 记录此时的左右端点
                x = s[left]  # 左端点字母
                if cnt[x] == 0:
                    # x 移出窗口之前,检查出现次数,
                    # 如果窗口内 x 的出现次数和 t 一样,
                    # 那么 x 移出窗口后,窗口内 x 的出现次数比 t 的少
                    less += 1
                cnt[x] += 1  # 左端点字母移出子串
                left += 1
        return "" if ans_left < 0 else s[ans_left: ans_right + 1]
相关推荐
chushiyunen1 小时前
redis命令 geo(对地理坐标的支持)
数据库·redis·缓存
AI大模型学徒1 小时前
NLP基础(八)_马尔可夫模型
算法·机器学习·自然语言处理·nlp·概率论·马尔可夫模型
精英的英1 小时前
【嵌入式Linux开发】如何在Windows上开发Linux ARM版本QT程序
linux·arm开发·windows
咯哦哦哦哦1 小时前
linux patchelf工具 用法
linux·vscode·编辑器·gcc
努力的小帅1 小时前
Linux_进程控制(Linux入门到精通)
linux·网络·shell·进程创建·linux入门
睡觉然后上课1 小时前
如何让虚拟机运行速度翻倍
linux·arm开发·windows
喜欢你,还有大家2 小时前
DaemonSet && service && ingress的
linux·架构·kubernetes
前端小L2 小时前
图论专题(十八):“逆向”拓扑排序——寻找图中的「最终安全状态」
数据结构·算法·安全·深度优先·图论·宽度优先
liu_bees2 小时前
Jenkins 中修改 admin 账号密码的正确位置与方法
java·运维·tomcat·jenkins