#LC388. 文件的最长绝对路径

文件的最长绝对路径

你好,同学。欢迎来到 NOI 专题训练。

今天我们要挑战的是一个非常有意思的模拟题:文件的最长绝对路径。这道题虽然来源于工程应用(文件系统遍历),但其内核是考察选手对树的深度优先搜索(DFS)栈(Stack)以及复杂字符串解析的综合掌控能力。


一、 预备知识点

在挑战本题前,请确保你已经掌握:

  1. 栈(Stack)或前缀和思想:用于维护当前路径的累积长度。
  2. 字符串解析(String Parsing)
    • 识别转义字符 \n(换行,表示新文件/目录的开始)。
    • 识别转义字符 \t(制表符,表示当前文件/目录的深度)。
  3. 深度优先搜索逻辑:理解目录结构的层次感,每一层目录的长度都依赖于上一层。

二、 NOI 竞赛题目描述

题目名称:文件的最长绝对路径 (Longest Absolute File Path) 时间限制:1.0s 内存限制:256MB

【问题描述】 假设文件系统可以用字符串表示。例如,字符串 "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" 表示:

dir
    subdir1
    subdir2
        file.ext

这里 dir 是根目录,包含两个子目录 subdir1subdir2subdir2 包含一个文件 file.ext

我们感兴趣的是文件系统中某个文件(即名称中包含 . 的项)的绝对路径的长度。在上面的例子中,绝对路径是 "dir/subdir2/file.ext",其长度为 2020

给你一个表示文件系统的字符串 input,返回文件系统中 最长绝对路径 的长度。如果系统中没有文件,返回 00

注意:

  • 名称中包含 . 的项被视为文件,否则视为目录。
  • \n\t 仅占一个字符长度(在输入中可能以转义字符形式出现)。

【输入格式】 输入仅一行,包含一个长度为 LL 的字符串 SS(注意:在 NOI 评测中,输入字符串可能包含空格,建议使用 getline 或处理转义逻辑)

【输出格式】 输出一个整数,表示最长的绝对路径长度。

【样例输入 1】

dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext

【样例输出 1】

20

【样例输入 2】

dir\n\tsubdir1\n\t\tsubdir11\n\tsubdir2\n\t\tsubdir21\n\t\t\tfile.ext

【样例输出 2】

25

【数据规模与约定】

  • 1L1051 \le L \le 10^5
  • input 由英文字母、数字、空格、'.'、'\n' 和 '\t' 组成。

三、 启发式思路引导:草稿纸上的推演

请拿出草稿纸,我们通过“层级长度累加法”来思考:

1. 核心矛盾:如何知道当前文件在哪一层?

  • 观察\t 的数量直接决定了深度。0 个 \t 是第 0 层(根),1 个 \t 是第 1 层。
  • 启发:我们可以维护一个数组(或栈) depth_length,其中 depth_length(i) 表示第 i 层目录的累积绝对路径长度

2. 草稿纸模拟过程(以样例 1 为例)

字符串:dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext

  1. 遇到 "dir"
    • 深度:0
    • 名字长度:3
    • 当前是目录。记录 level_len(0) = 3
  2. 遇到 "\tsubdir1"
    • 深度:1(通过 1 个 \t 识别)
    • 名字长度:7
    • 绝对路径:level_len(0) + / + subdir1 \rightarrow 3+1+7=113 + 1 + 7 = 11
    • 是目录。记录 level_len(1) = 11
  3. 遇到 "\tsubdir2"
    • 深度:1
    • 名字长度:7
    • 绝对路径:level_len(0) + / + subdir2 \rightarrow 3+1+7=113 + 1 + 7 = 11
    • 是目录。覆盖记录 level_len(1) = 11
  4. 遇到 "\t\tfile.ext"
    • 深度:2
    • 名字长度:8
    • 绝对路径:level_len(1) + / + file.ext \rightarrow 11+1+8=2011 + 1 + 8 = 20
    • 关键:含有 .,它是文件!更新全局最大值 ans = max(0, 20) = 20

四、 算法流程图(伪代码逻辑)

在 C++14 实现中,由于字符串可能很大,建议一次性扫描或使用迭代器。

graph TD
    Start(开始扫描 input 字符串) --> Init(初始化 ans=0, level_len 数组)
    Init --> Loop{是否到达末尾}
    Loop -- 否 --> FindLevel(计算当前行开头的 tab 数量记为 depth)
    FindLevel --> GetName(提取文件名或目录名记为 name)
    GetName --> CheckFile{name 中是否包含点号}
    
    CheckFile -- 是 --> UpdateAns(计算当前总长度并更新 ans)
    CheckFile -- 否 --> UpdateLen(更新 level_len_at_depth 并存入数组)
    
    UpdateAns --> Loop
    UpdateLen --> Loop
    Loop -- 是 --> Finish(返回 ans)

    subgraph Length_Calc
    UpdateAns_Logic(总长 = 上一层长度 + 1加上当前名字长度)
    end

五、 复杂度分析与优化建议

  • 时间复杂度O(L)O(L)
    • 我们只需要对字符串进行一遍线性扫描。
    • 虽然字符串切分可能产生临时对象,但在 C++ 中可以通过索引 start, end 避开昂贵的字符串拷贝。
  • 空间复杂度O(L)O(L)
    • 最坏情况下深度可以达到 LL(每一层只有一个字母),我们需要一个大小为 LL 的数组来记录每一层的长度。

优化建议

  1. 快速读入:在 NOI 竞赛中,如果字符串包含 \n 这种字符,直接用 cin >> s 会在换行处停止。请使用 while(getline(cin, s)) 或者通过 getchar() 逐字符解析。
  2. 原地解析:不要使用 split 函数(C++ 标准库没有自带高效的 split),通过双指针直接在原串上计算 \t 数量和名字长度。

六、 读题关键词总结

  1. "\n" 与 "\t":这是文件树的“导航仪”,决定了逻辑上的入栈和出栈。
  2. "包含 '.'":这是触发结算的唯一条件,只有文件计入答案,目录不计入。
  3. "绝对路径":别忘了层级之间的斜杠 /,它也占 1 个字符长度(通常在计算第 i 层长度时,如果 i > 0,则需要 +1)。

教练寄语: 这道题的核心在于状态维护。你不需要真的去构建一棵树,只需要记住“当前通往根的路径长是多少”。这种“化树为线”的思维,是解决高级模拟题的重要技巧。去尝试写出你的 C++ 代码吧!