package com.freetymekiyan.algorithms.level.medium; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.util.ArrayDeque; import java.util.Deque; /** * Suppose we abstract our file system by a string in the following manner: * <p> * The string "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" represents: * <p> * dir * subdir1 * subdir2 * file.ext * The directory dir contains an empty sub-directory subdir1 and a sub-directory subdir2 containing a file file.ext. * <p> * The string "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" represents: * <p> * dir * subdir1 * file1.ext * subsubdir1 * subdir2 * subsubdir2 * file2.ext * The directory dir contains two sub-directories subdir1 and subdir2. subdir1 contains a file file1.ext and an empty * second-level sub-directory subsubdir1. subdir2 contains a second-level sub-directory subsubdir2 containing a file * file2.ext. * <p> * We are interested in finding the longest (number of characters) absolute path to a file within our file system. For * example, in the second example above, the longest absolute path is "dir/subdir2/subsubdir2/file2.ext", and its length * is 32 (not including the double quotes). * <p> * Given a string representing the file system in the above format, return the length of the longest absolute path to * file in the abstracted file system. If there is no file in the system, return 0. * <p> * Note: * The name of a file contains at least a . and an extension. * The name of a directory or sub-directory will not contain a .. * Time complexity required: O(n) where n is the size of the input string. * <p> * Notice that a/aa/aaa/file1.txt is not the longest file path, if there is another path aaaaaaaaaaaaaaaaaaaaa/sth.png. */ public class LongestAbsoluteFilePath { private LongestAbsoluteFilePath l; /** * Stack. * Using stack to save previous length at each level. * Find current level by the last index of "\t" in filename + 1. * Compare current level with stack size, stack size should be one level larger than current level. * Calculate current length, with slash at the end. * Push this length into stack. * If current name is a file, update max. * The length here should minus 1 to remove the last "/". */ public int lengthLongestPath(String input) { Deque<Integer> stack = new ArrayDeque<>(); stack.push(0); // Dummy level, start from 0 String[] files = input.split("\n"); int max = 0; for (String f : files) { int level = f.lastIndexOf("\t") + 1; while (stack.size() > level + 1) { // Stack size is one level larger since we have a dummy level stack.pop(); } int len = stack.peek() + f.length() - level + 1; // Previous length + current length + "/" stack.push(len); if (f.contains(".")) { // Update max only when it is a file max = Math.max(max, len - 1); // Length without the last "/" } } return max; } @Before public void setUp() { l = new LongestAbsoluteFilePath(); } @Test public void testExamples() { Assert.assertEquals(20, l.lengthLongestPath("dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext")); Assert.assertEquals(32, l.lengthLongestPath("dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext")); } @After public void tearDown() { l = null; } }