Leetcode0388. 文件的最长绝对路径(medium, 栈, DFS)

目录

1. 问题描述

2. 解题分析

3. 代码实现


1. 问题描述

假设有一个同时存储文件和目录的文件系统。下图展示了文件系统的一个示例:

这里将 dir 作为根目录中的唯一目录。dir 包含两个子目录 subdir1 和 subdir2 。subdir1 包含文件 file1.ext 和子目录 subsubdir1subdir2 包含子目录 subsubdir2,该子目录下包含文件 file2.ext 。

在文本格式中,如下所示(⟶表示制表符):

dir
⟶ subdir1
⟶ ⟶ file1.ext
⟶ ⟶ subsubdir1
⟶ subdir2
⟶ ⟶ subsubdir2
⟶ ⟶ ⟶ file2.ext

如果是代码表示,上面的文件系统可以写为 "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" 。'\n' 和 '\t' 分别是换行符和制表符。

文件系统中的每个文件和文件夹都有一个唯一的 绝对路径 ,即必须打开才能到达文件/目录所在位置的目录顺序,所有路径用 '/' 连接。上面例子中,指向 file2.ext 的 绝对路径 是 "dir/subdir2/subsubdir2/file2.ext" 。每个目录名由字母、数字和/或空格组成,每个文件名遵循 name.extension 的格式,其中 name 和 extension由字母、数字和/或空格组成。

给定一个以上述格式表示文件系统的字符串 input ,返回文件系统中 指向 文件 的 最长绝对路径 的长度 。 如果系统中没有文件,返回 0

示例 1:

输入:input = "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext"
输出:20
解释:只有一个文件,绝对路径为 "dir/subdir2/file.ext" ,路径长度 20

示例 2:

输入:input = "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext"
输出:32
解释:存在两个文件:
"dir/subdir1/file1.ext" ,路径长度 21
"dir/subdir2/subsubdir2/file2.ext" ,路径长度 32
返回 32 ,因为这是最长的路径

示例 3:

输入:input = "a"
输出:0
解释:不存在任何文件

示例 4:

输入:input = "file1.txt\nfile2.txt\nlongfile.txt"
输出:12
解释:根目录下有 3 个文件。
因为根目录中任何东西的绝对路径只是名称本身,所以答案是 "longfile.txt" ,路径长度为 12

提示:

  • 1 <= input.length <= 10^4
  • input 可能包含小写或大写的英文字母,一个换行符 '\n',一个制表符 '\t',一个点 '.',一个空格 ' ',和数字。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-absolute-file-path
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2. 解题分析

        最直观的做法是显式地构建出目录树,除根节点外每个目录或者叶子的节点存储它自己的字符串长度,根节点的值为0。然后再进行树的遍历求出每个叶子(文件)的总的路径长度:从根节点到它自己的各节点的值的总和。

        可不可以跳过显式地构建树的过程呢?

        如示例2的图所示,subdir1的所有子目录或文件一定出现在与它同级的subdir2的前面。同一个目录中,文件是不是一定排在子目录前面呢?这个在问题描述中没有明确说,暂时不作这个假定

        这事实上是一个目录树的前序遍历的展开,可以用类似于深度优先遍历的方式来求解。

        用一个栈来维护目录树的信息,首先将根目录信息入栈,其中包含绝对路径长度0和它的深度0。对目录树的文本格式表示从左到右扫描,以下分文件和目录两种情况分别考虑:

  • (1) 碰到文件的话(根据是否有由“.”分割的扩展名来判断),不入栈。查看栈顶节点的深度(有peek函数的话,可以用peek的方式不弹出栈顶而查询栈顶信息;如果没有peek函数的话,则可以先弹出再放回),如果不大于当前文件的深度,则将其退栈。重复,直到第一个深度更大的元素。将其绝对路径长度加上它自己的文件名长度即得它对应的绝对路径长度,与当前路径长度最大值比较并更新路径长度最大值。
  • (2) 碰到目录时,查询栈中的元素,如果栈顶元素的深度不大于当前目录的深度,则弹出;重复直到找到第一个深度更大的元素,基于这个元素的绝对路径长度计算当前目录的绝对路径长度,将当前目录的信息入栈
  • (3) 重复(1),(2)搜索过程直到末尾

计算目录的绝对路径长度时,除了根目录外,都要考虑其后加一个“/”所占位置。

以示例1为例进行说明,其处理步骤展开如下:

  • (1) 根目录入栈,其信息为(0,0),前者表示绝对路径长度,后者表示深度
  • (2) dir入栈,其信息为(4,1)。dir本身长度为3,再加上“/”长度变为4,因此绝对路径长度为4
  • (3) subdir1入栈,其信息为(12,2)
  • (4) subdir2. 由于subdir2的深度与此时栈顶subdir1深度相同,因此将subdir1弹出,将subdir2入栈,其信息为(12,2)
  • (5) file.txt,其自身长度为8,当前栈顶元素长度为12,因此它的绝对路径长度为20

        余下的细节问题就是如何从输入字符串中识别目录名和文件名以及目录所处深度。

        (1)以\n来作为一个有效名字的结尾的识别。为了方便最后一个名字的处理,先在input尾巴上追加一个\n

        (2)以下代码在搜索名字及其深度时事实上是实现了一个有限状态机,虽然没有显式地写成状态机的形式 

        

3. 代码实现

import time
from typing import List
from collections import deque

class Solution:
    def lengthLongestPath(self, input: str) -> int:
        
        input = input +'\n' # Attach one '\n' for the convenience of processing
        maxlen = 0
        q = deque([(0,0)]) # Put root node into stack

        tab_cnt = 0       
        name    = ''
        isFile  = False
        for k,c in enumerate(input):
            if c=='\t':
                tab_cnt += 1
            elif c=='\n':
                # Found a new name, either a dir name or a file name
                if isFile:
                    # A file
                    # print('A file is found: ',name)
                    while True:
                        length, layer = q.pop()
                        if layer < (tab_cnt+1):
                            filePathLen = len(name) + length
                            maxlen = filePathLen if filePathLen>maxlen else maxlen
                            q.append((length, layer)) # Put it back into stack
                            break
                else:
                    # A dir
                    # print('A dir is found: ',name)
                    while True:
                        length, layer = q.pop()
                        if layer < (tab_cnt+1):
                            dirPathLen = len(name) + length + 1
                            q.append((length, layer)) # Put it back into stack
                            q.append((dirPathLen,tab_cnt+1))
                            break
                # Reset 
                tab_cnt = 0
                name    = ''
                isFile  = False
            else:
                name = name + c
                if c=='.':
                    isFile = True
            
        return maxlen
                
if __name__ == "__main__":
    
    sln = Solution()
    input = "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext"
    print(sln.lengthLongestPath(input))
    
    input = "dir\n\tsubdir1\n\t\tfile.ext\n\tsubdir2\n\t\tfile.ext"
    print(sln.lengthLongestPath(input))
    
    input = "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext"
    print(sln.lengthLongestPath(input))

    input = "dir\n\tsubdir1\n\t\tsubsubdir1\n\t\tfile1.ext\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext"
    print(sln.lengthLongestPath(input))
    
    input = "a"
    print(sln.lengthLongestPath(input))
    
    input = "file1.txt\nfile2.txt\nlongfile.txt"
    print(sln.lengthLongestPath(input))

        执行用时:40 ms, 在所有 Python3 提交中击败了37.35%的用户

        内存消耗:15.1 MB, 在所有 Python3 提交中击败了27.24%的用户

        这个题不是那种特别规整的一眼就看出解决方案,所以思考方案本身花了不少时间,但是一旦想清楚了以后,代码到提交一气呵成一把通过,虽然性能不怎么的,心里还是有点小得意^-^.

        瞄了一眼官解,感觉比我想的要复杂,没耐心看了。

        回到主目录:笨牛慢耕的Leetcode每日一解题解笔记(动态更新。。。)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

笨牛慢耕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值