Leetcode 1598. Crawler Log Folder

Problem Description

The problem presents a Leetcode file system that stores user actions in a log. The user can perform various operations: move to the parent folder ("../"), stay in the current folder ("./"), or move to a specified child folder ("x/"). You start at the main folder and are given a list of operations that the users performed. Your task is to find the minimum number of operations you need to take to return back to the main folder.

For example, consider the following operations: logs = ["d1/","d2/","../","d21/","./"] To return back to the main folder, you would need to travel back 2 times (through the "../" operation). Therefore, the output in this case would be 2.

Solution Approach

The solution to this problem involves traversing the logs list and keeping track of your position relative to the main folder. If you see a "../" operation, you move one step closer to the main folder (unless you're already at the main folder, in which case you stay). If you see a "./" operation, you stay in the same folder. If you see an "x/" operation, you go deeper into the file system by moving to the child folder. After traversing the entire list of logs, you return your position, which represents the number of steps you need to take to return to the main folder.

Python

1
2python
3class Solution:
4    def minOperations(self, logs):
5        depth = 0
6        # iterate over the logs
7        for log in logs:
8            # if log operation is '../', move up unless already at the root
9            if log == '../': 
10                depth = max(0, depth - 1)
11            # if log operation is './', stay put
12            elif log == './':
13                continue
14            # otherwise move down to the child directory
15            else:
16                depth += 1
17        return depth

Java

1
2java
3class Solution {
4    public int minOperations(String[] logs) {
5        int depth = 0;
6        for (String log : logs) {
7            if (log.equals("../")) {
8                depth = Math.max(0, depth - 1);
9            } else if (!log.equals("./")) {
10                depth++;
11            }
12        }
13        return depth;
14    }
15}

JavaScript

1
2javascript
3class Solution {
4    minOperations(logs) {
5        let depth = 0;
6        for (let log of logs) {
7            if (log === '../') {
8                depth = Math.max(0, depth - 1);
9            } else if (log !== './') {
10                depth++;
11            }
12        }
13        return depth;
14    }
15}

C++

1
2cpp
3class Solution {
4public:
5    int minOperations(vector<string>& logs) {
6        int depth = 0;
7        for (string log : logs) {
8            if (log == "../") {
9                depth = max(0, depth - 1);
10            } else if (log != "./") {
11                depth++;
12            }
13        }
14        return depth;
15    }
16};

C#

1
2csharp
3public class Solution {
4    public int MinOperations(string[] logs) {
5        int depth = 0;
6        foreach (string log in logs) {
7            if (log == "../") {
8                depth = Math.Max(0, depth - 1);
9            } else if (log != "./") {
10                depth++;
11            }
12        }
13        return depth;
14    }
15}

Note: Here, we are using the standard libraries of each language to dynamically adjust the depth level. Also, in all these implementations, the time complexity is O(n), where n is the number of logs because we are scanning through the list of logs once. The space complexity is O(1) because we are using a constant amount of space (for the "depth" variable) irrespective of the size of the logs array.# Explanation In all of the solutions described above, the goal is to keep track of the current depth, or how many directories above the main folder the user is in, and adjust it accordingly based on user operations.

Each kind of operation has a different effect:

  • Whenever the user goes to a child directory, denoted by strings of the form "name/", the depth is increased by 1.
  • When the user stays in the current directory, denoted by the string "./", the depth does not change.
  • Finally, when the user goes back to the parent directory, denoted by the string "../", if the depth is larger than 0, it is reduced by one (since the user has moved one directory closer to the main folder).

The solution goes through the list of operations and modifies the depth based on each operation, from left to right. When all operations have been processed, the remaining depth is the minimum number of "../" operations needed to return to the main folder. Since each operation is processed only once, and the total number of operations determines the time it takes to process all operations, the time complexity is O(n).

Conclusion

The problem presents a simplified model of directory navigation. The case of going back to the parent of the main folder (which cannot normally be done in a real file system) is handled by assuming that this operation has no effect. A deeper analysis could take into account directory names and enforce the rule that a "../" operation only has an effect if there is a matching "name/" operation before it. However, this complicates the solution and increases both the time and space complexity. Therefore, under the given conditions and assumptions, the solutions presented here in Python, Java, JavaScript, C++, and C#, which all have a time complexity of O(n) and a space complexity of O(1), are optimal.


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫