题目描述

给定字符串 s,其中单词之间使用完整子串 %20 分隔。请反转单词的排列顺序,并返回处理后的字符串。

每个单词内部的字符顺序保持不变。输出首尾不保留分隔符,相邻单词之间恰好使用一个 %20;连续分隔符形成的空片段不作为单词。

示例 1:

输入:s = "hello%20world%20java"
输出:"java%20world%20hello"

示例 2:

输入:s = "%20hello%20%20world%20"
输出:"world%20hello"

提示:

  • %20 是完整的三字符分隔符,不对其他 URL 编码进行解码。
  • %20 以外的字符仍属于单词内容。
  • 没有有效单词时返回空字符串。

题意分析

本篇按 %20 分隔单词的约定处理:反转单词的先后顺序,保留每个单词内部的内容;输出首尾不含分隔符,相邻单词之间仅保留一个 %20。

这里的分隔符是连续三个字符组成的完整子串。处理目标是单词顺序,其他字符仍属于单词内容,无需解析或解码整个 URL。

解法:按 %20 切分后逆序拼接

核心思路

[!blue]

先按完整的 %20 切分字符串。每个非空片段对应一个单词,片段内部的字符没有被改动,得到的片段顺序也与原串词序相同。

首尾分隔符和连续分隔符可能产生空片段,它们不代表实际单词,应在切分后过滤掉。这样剩余列表只保存单词,后续无需再区分多余分隔符原来位于哪里。

接下来把单词列表倒序输出,再用一个 %20 连接相邻单词。每个单词恰好出现一次,原来靠后的单词先输出,因此词序被反转;只调整片段顺序,不反转片段内容,词内字符保持原样。

Java 从单词列表末尾向前追加,只有结果非空时才先补分隔符。Go 先交换首尾单词、逐步向中间移动,再用 strings.Join 连接。两种写法都只在两个真实单词之间放分隔符;没有单词时自然得到空串。

解题步骤

  1. 用完整子串 %20 切分输入,获得按原顺序排列的片段。
  2. 逐个检查片段,只把非空片段加入单词列表。
  3. Java 倒序遍历单词列表,按需补 %20 后追加单词;Go 用双指针反转列表,再以 %20 连接。
  4. 返回结果。只有一个单词时保持原样,没有单词时返回空串。

代码实现

class Solution {
    public String reverseWords(String s) {
        String[] parts = s.split("%20");
        List<String> words = new ArrayList<>();

        for (String part : parts) {
            // 过滤完整分隔符产生的空片段,统一处理首尾和连续分隔
            if (!part.isEmpty()) {
                words.add(part);
            }
        }

        StringBuilder answer = new StringBuilder();

        for (int i = words.size() - 1; i >= 0; i--) {
            // 仅在两个真实单词之间追加一个分隔符
            if (answer.length() > 0) {
                answer.append("%20");
            }

            answer.append(words.get(i));
        }

        return answer.toString();
    }
}
import "strings"

func reverseWords(s string) string {
    parts := strings.Split(s, "%20")
    words := make([]string, 0, len(parts))
    for _, part := range parts {
        // 过滤完整分隔符产生的空片段,统一处理首尾和连续分隔
        if part != "" {
            words = append(words, part)
        }
    }

    // 先反转单词列表,再由连接函数插入统一分隔符
    for left, right := 0, len(words)-1; left < right; left, right = left+1, right-1 {
        words[left], words[right] = words[right], words[left]
    }
    return strings.Join(words, "%20")
}

复杂度分析

设输入长度为 n,实际单词数为 w。

  • 时间复杂度:$O(n)$。分隔符长度固定,切分及过滤所有片段共需线性时间;反转单词顺序为 $O(w)$,拼接的总字符数不超过原串长度。
  • 空间复杂度:$O(n)$。切分结果、单词列表及输出缓冲区总共使用线性空间。

关键点总结

[!green]

  • 识别的是完整 %20,不能把它拆成单字符分隔规则。
  • 空片段只表示多余分隔符,过滤后才得到真正的单词序列。
  • 反转单词列表的顺序,并按固定分隔符重组,就能同时保留词内内容和规范词间间隔。

易错点总结

[!yellow]

  • 按 %、2 或 0 单个字符切分,会破坏本来属于单词的内容。
  • 保留空片段参与连接,会把首尾或连续分隔符重新带回结果。
  • 直接反转所有字符,会同时反转单词内部和 %20 标记。
  • 先做通用 URL 解码,会改变其他编码字符,不符合本篇保留单词内容的约定。
  • 每个单词后都追加 %20 会留下尾部分隔符;应只在相邻单词之间插入。

相似题目

题目 难度 关联与区别
151. 反转字符串中的单词 中等 单词顺序翻转相同,本题分隔符是完整的%20标记,而不是单个空格字符。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2024/90916200
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!