SimplifyPath [source code]
public class SimplifyPath {
static
/******************************************************************************/
class Solution {
public String simplifyPath(String path) {
Deque<String> stack = new ArrayDeque<> ();
char[] chp = path.toCharArray ();
if (chp.length <= 1)
return path;
for (int i = 0; i < chp.length; i++) {
if (chp[i] != '/') {
int poke = i;
while (poke < chp.length && chp[poke] != '/')
poke++;
String token = new String (chp, i, poke - i);
i = poke;
if (token.equals (".")) {
continue;
} else if (token.equals ("..")) {
if (!stack.isEmpty ())
stack.pop ();
} else {
stack.push (token);
}
}
}
if (stack.isEmpty ())
return chp[0] == '/' ? "/" : "";
StringBuilder sb = new StringBuilder ();
while (!stack.isEmpty ()) {
if (sb.length () > 0 || chp[0] == '/')
sb.append ("/");
sb.append (stack.pollLast ());
}
return sb.toString ();
}
}
/******************************************************************************/
public static void main(String[] args) {
SimplifyPath.Solution tester = new SimplifyPath.Solution();
String[] inputs = {
"/home/", "/home",
"/a/./b/../../c/", "/c",
"/../", "/",
"/home//foo/", "/home/foo",
};
for (int i = 0; i < inputs.length / 2; i++) {
String path = inputs[2 * i], ans = inputs[2 * i + 1];
System.out.println (Printer.separator ());
String output = tester.simplifyPath (path);
System.out.printf ("%s -> %s, expected: %s\n",
path, Printer.wrap (output, output.equals (ans) ? 92 : 91), ans
);
}
}
}
downvote很多的一个题目, 准备好被虐了;
给的两个Corner Case的第一个好像还是有点印象的, OS的时候学过的, 知识点就是root的.
和..
都是root(/
)自己;
感觉这个题最后难度估计就是各种case的break了, 算法本身并不复杂;
自己写的时候很想用split来直接写, 不过想想真正面试的时候碰到这题很可能会让你自己手动扫描, 还是练一练算了;
另外, Stack算法, 在pop之前先判断peek, 这个也是见过的小技巧了, 其实也可以理解为一个pre leaf的判断;
另外提醒一点, 这题是一个让你simplify的过程, 不是让你跟Pintos上面那样, 去parse; Pintos当时还要先判断一下起点在哪里; 这里其实也有一个稍微类似的相应的处理: 并不是所有的/
都是可以直接跳过的;
仔细考虑了之后, 写了上面的算法, 就把题目给的四个test给调出来之后, 一次就AC了, 勉强没有超时; 速度是5ms (98%), 很意外的快;
另外, 看上面用的这个String(char[] value, int offset, int count)
函数; 这个函数对于我这种喜欢用char array的选手来说, 还是要熟悉的, 经常需要用到; 注意arg2是count, 而不是end_index;
另外, StringBuilder没有isEmpty ()
和size ()
. 这个以前还真的没有注意过;
试了一下, 才发现这个题目是不handle relative path的; 反正我的算法是可以handle的; 不过既然题目要求简化了, 按理说应该说清楚的; 哦, 人家有说是absolute path, 我自己没看清; 算了, 多写点也不是过错;
在后面调的玩的时候, 发现这题居然还有"/..." -> "/..."
这样的test; 这个就很无语了; 不过这个其实没有毛病, 本身文件名是允许包含.
的, 比如extension;
没有editorial;
@monaziyi said in C++ 10-lines solution:
C++ also have getline which acts like Java's split. I guess the code can comment itself.
string simplifyPath(string path) {
string res, tmp;
vector<string> stk;
stringstream ss(path);
while(getline(ss,tmp,'/')) {
if (tmp == "" or tmp == ".") continue;
if (tmp == ".." and !stk.empty()) stk.pop_back();
else if (tmp != "..") stk.push_back(tmp);
}
for(auto str : stk) res += "/"+str;
return res.empty() ? "/" : res;
}
其实还是很好理解的, 这个getline
好像就跟c的strtok_r
还是什么的; 因为人家题目读的仔细, 没有考虑relative path的问题, 所以最后实际上的算法本身还是很简单的;
另外, 他这个代码因为不当的使用了&& in header, 导致代码有一个地方比较蠢, 这个我自己是总结过的:
@rabeeh said in C++ 10-lines solution:
you do not need to check for .. two times, you need to put checking for empty inside the if
class Solution {
public:
string simplifyPath(string path) {
string result="", token;
stringstream ss(path);
vector<string> tokens;
while(getline(ss, token, '/')){
if(token=="." || token=="") continue;
else if(token==".."){
if(tokens.size()!=0) tokens.pop_back();
}
else tokens.push_back(token);
}
if(tokens.size()==0) return "/";
for(int i=0; i<tokens.size(); ++i)
result=result+'/'+tokens[i];
return result;
}
};
所以我再次重申, 在header里面的条件并列with && or ||
- 要么是你确定是一个明显的and或者or的关系;
- 要么是你故意用short circuit来保护后面的condition;
感觉这个总结还是不够完整; 重要的其实还是有一个logic tree的概念: !stk.empty()
这个本身就不跟tmp == ".."
是一个level, 所以不要放到一个header里面;
@shpolsky said in Java 10-lines solution with stack:
Hi guys!
The main idea is to push to the stack every valid file name (not in {"",".",".."}), popping only if there's smth to pop and we met "..". I don't feel like the code below needs any additional comments.
public String simplifyPath(String path) {
Deque<String> stack = new LinkedList<>();
Set<String> skip = new HashSet<>(Arrays.asList("..",".",""));
for (String dir : path.split("/")) {
if (dir.equals("..") && !stack.isEmpty()) stack.pop();
else if (!skip.contains(dir)) stack.push(dir);
}
String res = "";
for (String dir : stack) res = "/" + dir + res;
return res.isEmpty() ? "/" : res;
}
Hope it helps!
原来LinkedList也有Deque的接口;
@lym5523 said in Java 10-lines solution with stack:
I guess the reason is that stack iterator will not iterate in order as you expected. See this: http://stackoverflow.com/questions/16992758/is-there-a-bug-in-java-util-stacks-iterator
我也被坑过;
submission最优解: 3(99):
class Solution {
public String simplifyPath(String path) {
char[] res = new char[path.length() + 1];
char[] input = path.toCharArray();
int slow = 0;
int fast = 0;
if (input[0] != '/') {
res[0] = '/';
slow = 1;
}
while (fast < path.length()) {
if (input[fast] == '/') {
res[slow++] = input[fast++];
while (fast < input.length && input[fast] == '/') {
fast++;
}
} else if (input[fast] != '.' && input[fast] != '/') {
while (fast < input.length && input[fast] != '/') {
res[slow++] = input[fast++];
}
} else if (input[fast] == '.') {
if (fast + 1 == path.length()) {
break;
}
if (input[fast + 1] == '/') {
fast += 1;
while (fast < input.length && input[fast] == '/') {
fast++;
}
} else if (input[fast + 1] != '.' && input[fast + 1] != '/') {
while (fast < input.length && input[fast] != '/') {
res[slow++] = input[fast++];
}
} else if (input[fast + 1] == '.') {
if (fast + 2 == path.length()) {
if (slow >= 2) {
slow -= 2;
}
while (slow > 0 && res[slow] != '/') {
slow--;
}
break;
}
if (input[fast + 2] == '/') {
if (slow >= 2) {
slow -= 2;
}
while (slow > 0 && res[slow] != '/') {
slow--;
}
fast += 2;
} else {
while (fast < input.length && input[fast] != '/') {
res[slow++] = input[fast++];
}
}
}
}
}
if (slow > 1 && res[slow - 1] == '/') {
slow -= 1;
}
return slow == 0 ? "/" : new String(res, 0, slow);
}
}
又臭又长, 不看了, 大概意思就是直接一边parse一边往result里面丢; 不知道他是怎么处理/a/b/c/../../..
这种的, 怎么往回undo呢? 试了一下, 他的这个代码是能搞定的;
哦, 看了一下知道了, 他直接往里面丢的时候, 是同时把/
也丢进去的, 所以最后需要倒退的时候, 直接扫到/
的位置就行了;
这种算法看看就行了; 可读性太差了;
Problem Description
Given an absolute path for a file (Unix-style), simplify it.
For example,
path = "/home/", => "/home"
path = "/a/./b/../../c/", => "/c"
click to show corner cases.
Corner Cases:
- Did you consider the case where path = "/../"?
In this case, you should return "/". - Another corner case is the path might contain multiple slashes '/' together, such as "/home//foo/".
In this case, you should ignore redundant slashes and return "/home/foo".
Difficulty:Medium
Total Accepted:107.1K
Total Submissions:409.6K
Contributor:LeetCode
Companies
facebookmicrosoft
Related Topics
stringstack