跳转至

BM83 字符串变形

简单通过率 22.69%python3核心代码模式样例通过牛客 AC字符串

牛客原题  源码

讲解章节:字符串处理

一句话

整串反转大小写,再把空格切出的各段整体逆序拼回去。

解题思路

这题考什么

两条互不干扰的规则叠在同一个串上,拆开分别执行即可:

1. 每个字母大小写互换(空格不变)
2. 由空格隔开的单词整体逆序,单词内部的字符顺序不变

第 1 步逐字符处理,同一字母的大小写 ASCII 码相差 32,Python 里就是 str.swapcase()。

第 2 步的全部难度在空格上。题面只说「空格隔开」,没说空格只有一个, 首尾也可能有空格,而变形后这些空格要原样出现在镜像位置。 不带参数的 str.split() 按「连续空白」切分并丢弃首尾空白, 空格的数量信息会被整个吃掉;str.split(" ") 按单个空格切分, 连续空格之间留下空串,首尾空格各留一个空串, 空格的个数与分布因此被完整编码进列表里:

"a  b "  --swapcase-->  "A  B "
         --split(" ")-> ["A", "", "B", ""]
         --逆序------->  ["", "B", "", "A"]
         --" ".join-->  " B  A"

手写版本等价于从右往左扫,遇到空格就把刚攒出的一段吐出来、空格照抄, 输出完全一致,但要自己管三处边界(段首、段尾、连续空格),更容易写错。

数据规模与复杂度

n <= 1e6,时限「其他语言 2 秒」。swapcase、split、逆序、join 各扫一遍, 时间 O(n)、空间 O(n)。字符串不可变,返回值必然是新对象, 空间做不到 O(1),题面的进阶要求也只写到 O(n)。 真正会超时的是「每凑出一个单词就 res = word + " " + res」这类前插拼接: 每次拼接都要重新分配并拷贝整串,n = 1e6 时搬运量是 O(n^2) 量级。

坑在哪

  1. 必须用 s.split(" ") 而不是 s.split()。后者会把连续空格与首尾空格一并吞掉, "a b " 输出成 "b a",少了两处空格——这是本题最常见的错解。
  2. 拼回去只能用单个空格 " ".join。空串占位与单空格分隔是配套的一整套编码, 换成 "".join 或多空格分隔,空格总数立刻对不上。
  3. 逆序作用在切分后的列表上,不是作用在字符上。 写成 flipped[::-1] 会把每个单词内部的字符也倒过来,"tHIS" 变 "SIHt"。
  4. 参数 n 是牛客模板带上的串长,除了开头的短路之外用不到; 切片与索引一律以 len(s) 为准,不要假定 n 与实际长度一致。

样例复核

"This is a sample" --swapcase--> "tHIS IS A SAMPLE" --split(" ")--> ["tHIS", "IS", "A", "SAMPLE"] --逆序--> ["SAMPLE", "A", "IS", "tHIS"] --" ".join--> "SAMPLE A IS tHIS",与样例一致。

参考实现

solutions/nowcoder/BM83/sol.py
from typing import List, Optional


class Solution:
    def trans(self, s: str, n: int) -> str:
        # 空串没有可变形的内容,原样返回
        if n == 0:
            return s
        # 第一步:整串大小写互换,空格与其它字符不受影响
        flipped = s.swapcase()
        # 第二步:按**单个**空格切分,连续空格与首尾空格各留下空串占位;
        # 整个列表逆序后再用单空格拼回,空格的个数与分布被镜像地保留下来
        return " ".join(flipped.split(" ")[::-1])
[:octicons-arrow-left-16: BM82](BM82.md) [BM84 :octicons-arrow-right-16:](BM84.md)