算法细节系列(34):再见字符串(2)
2017-06-13 10:00
295 查看
算法细节系列(34):再见字符串(2)
详细代码可以fork下Github上leetcode项目,不定期更新。题目摘自leetcode:
Leetcode 071. Simplify Path
Leetcode 468. Validate IP Address
Leetcode 165. Compare Version Numbers
Leetcode 068. Text Justification
Leetcode 151. Reverse Words in a String
Leetcode 071. Simplify Path
思路:主要遇到三种符号
a. ".." b. "." c. "dir" 第一种情况模拟返回 第二种情况无作为 第三种情况进入文件夹下 主要是遇到"..",所以我们用stack来模拟这种返回上一层的操作
代码如下:
public String simplifyPath(String path) { Deque<String> stack = new ArrayDeque<>(); char[] cs = path.toCharArray(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < path.length(); ++i){ if (cs[i] == '/'){ if (i + 1 < path.length() && cs[i+1] == '/') i++; String dir = sb.toString(); if (dir.isEmpty()) continue; if (dir.equals("..")){ if (!stack.isEmpty()) stack.pop(); } else if (dir.equals(".")){ } else{ stack.push(dir); } sb = new StringBuilder(); } else{ sb.append(cs[i]); } } if (sb.length() != 0){ String dir = sb.toString(); if (dir.equals("..")){ if (!stack.isEmpty()) stack.pop(); } else if (dir.equals(".")){ } else{ stack.push(dir); } } sb = new StringBuilder(); while (!stack.isEmpty()){ sb.append("/" + stack.pollLast()); } return sb.toString().isEmpty() ? "/" : sb.toString(); }
上面这代码够丑的,优化下,我用前一个”/”和下一个”/”作为读取数据的标志,所以在for循环结束还需要多判断一次。
我们可以用java自带的split方法进行简化:
public String simplifyPath(String path) { Deque<String> stack = new LinkedList<>(); Set<String> omit = new HashSet<>(Arrays.asList("..",".","")); for (String dir : path.split("/")){ if (dir.equals("..") && !stack.isEmpty()) stack.pop(); else if (!omit.contains(dir)) stack.push(dir); } StringBuilder sb = new StringBuilder(); while (!stack.isEmpty()) sb.append("/" + stack.pollLast()); return sb.length() == 0 ? "/" : sb.toString(); }
Leetcode 468. Validate IP Address
找规则解析就好了,corner case比较多,要有耐心,代码如下:String IPv4 = "IPv4"; String IPv6 = "IPv6"; public String validIPAddress(String IP) { if (IP.isEmpty()) return "Neither"; String ans = IP.contains(".") ? IPv4 : IPv6; if (ans.equals(IPv4)){ ans = validIPv4(IP) ? ans : "Neither"; } else{ ans = validIPv6(IP) ? ans : "Neither"; } return ans; } private boolean validIPv4(String IP){ if (IP.charAt(IP.length()-1) == '.' || IP.charAt(0) == '.') return false; String[] nums = IP.split("\\."); if (nums.length != 4) return false; for (String num : nums){ char[] c = num.toCharArray(); if (num.isEmpty() || (c[0] == '0' && c.length != 1)) return false; int n = 0; int i = 0; while (i < c.length && c[i] >= '0' && c[i] <= '9'){ n = 10 * n + c[i++] - '0'; } if (!(n >= 0 && n <= 255) || i < c.length) return false; } return true; } private boolean validIPv6(String IP){ if (IP.charAt(IP.length()-1) == ':' || IP.charAt(0) == ':') return false; String[] nums = IP.split(":"); if (nums.length != 8) return false; for (String num : nums){ if (num.isEmpty()) return false; char[] c = num.toCharArray(); if (c.length >= 5) return false; for (int i = 0; i < c.length; ++i){ if (! (c[i] >= '0' && c[i] <= '9' || c[i] >= 'a' && c[i] <= 'f' || c[i] >= 'A' && c[i] <= 'F')) return false; } } return true; }
Leetcode 165. Compare Version Numbers
版本的比较,有点类似基数排序,很简单,先split(“\.”),遇到大小相等的版本号(同一级别下),进入下一级别的比较。代码如下:
public int compareVersion(String version1, String version2) { String[] v1 = version1.split("\\."); String[] v2 = version2.split("\\."); int min = Math.min(v1.length, v2.length); int i = 0; while (i < min && Integer.parseInt(v1[i]) == Integer.parseInt(v2[i])) i++; if (i == min){ if (v1.length > v2.length){ for (int j = i; j < v1.length; ++j){ if (Integer.parseInt(v1[j]) != 0) return 1; } } else if (v1.length < v2.length){ for (int j = i; j < v2.length; ++j){ if (Integer.parseInt(v2[j]) != 0) return -1; } } return 0; } return Integer.parseInt(v1[i]) > Integer.parseInt(v2[i]) ? 1 : -1; }
代码简化思路:
version1 = "1.0.1.3.4.5" version2 = "1.0" 可以看成: version2 = "1.0.0.0.0.0"
代码如下:
public int compareVersion(String version1, String version2) { String[] v1 = version1.split("\\."); String[] v2 = version2.split("\\."); int max = Math.max(v1.length, v2.length); for (int i = 0; i < max; ++i){ int num1 = i < v1.length ? Integer.parseInt(v1[i]) : 0; int num2 = i < v2.length ? Integer.parseInt(v2[i]) : 0; if (num1 < num2) return -1; if (num1 > num2) return 1; } return 0; }
Leetcode 068. Text Justification
贪心算法,比较细节,代码逻辑较复杂。代码如下:(第一版)
public List<String> fullJustify(String[] words, int maxWidth) { List<String> ans = new ArrayList<>(); if (words.length == 0 || maxWidth == 0){ ans.add(""); return ans; } String res = greedy(words, maxWidth); while (!res.isEmpty()){ ans.add(res); res = greedy(words, maxWidth); } return ans; } int pos = 0; private String greedy(String[] words, int maxWidth){ if (pos == words.length) return ""; String ans = words[pos]; int cnt = ans.length(); for (int i = pos + 1; i < words.length; ++i){ if (cnt + words[i].length() + 1 <= maxWidth){ ans += " " + words[i]; cnt = ans.length(); pos = i + 1; } else{ pos = i; int space = maxWidth - cnt; String[] all = ans.split(" "); int len = all.length - 1; StringBuilder sb = new StringBuilder(); if (len == 0){ sb.append(all[0]); for (int k = 0; k < space; ++k){ sb.append(" "); } return sb.toString(); } if (space % len == 0){ for (int k = 0; k < all.length; ++k){ sb.append(all[k] + " "); for (int l = 0; l < space / len; ++l){ sb.append(" "); } } return sb.toString().trim(); } else{ int re = space % len; int sp = space / len; for (int k = 0; k < all.length; ++k){ sb.append(all[k] + " " + ((re != 0) ? " " : "")); re = Math.max(--re,0); for (int l = 0; l < sp; ++l){ sb.append(" "); } } return sb.toString().trim(); } } } pos = words.length; int space = maxWidth - cnt; String[] all = ans.split(" "); int len = all.length - 1; StringBuilder sb = new StringBuilder(); if (len == 0){ sb.append(all[0]); for (int k = 0; k < space; ++k){ sb.append(" "); } return sb.toString(); } sb = new StringBuilder(ans); for (int k = 0; k < space; ++k){ sb.append(" "); } return sb.toString(); }
for循环可以用while替代,一次找到需要组合的words,这样for中else的逻辑和for循环外的逻辑可以合并。
代码如下:(第二版本)
int pos = 0; private String greedy(String[] words, int maxWidth){ if (pos == words.length) return ""; int cnt = words[pos].length(); int i = pos + 1; while (i < words.length && cnt + words[i].length() + 1 <= maxWidth){ cnt += words[i++].length() + 1; } int space = maxWidth - cnt; int len = i - pos - 1; if (len == 0 || i == words.length){ StringBuilder sb = new StringBuilder(); for (int k = pos; k < i; ++k){ sb.append(words[k] + " "); } sb.deleteCharAt(sb.length()-1); for (int k = 0; k < space; ++k){ sb.append(" "); } pos = i; return sb.toString(); } StringBuilder sb = new StringBuilder(); if (space % len == 0){ for (int k = pos; k < i; ++k){ sb.append(words[k] + " "); for (int l = 0; l < space / len; ++l){ sb.append(" "); } } pos = i; return sb.toString().trim(); } else{ int re = space % len; int sp = space / len; for (int k = pos; k < i; ++k){ sb.append(words[k] + " " + ((re != 0) ? " " : "")); re = Math.max(--re,0); for (int l = 0; l < sp; ++l){ sb.append(" "); } } pos = i; return sb.toString().trim(); } }
单独开一个greedy方法也没谁了,还定义了全局变量pos,与其这样,不如放在一个方法内。
代码如下:(第三版本)
public List<String> fullJustify(String[] words, int maxWidth) { List<String> ans = new ArrayList<>(); int pos = 0; while (pos < words.length){ int cnt = words[pos].length(); int i = pos + 1; while (i < words.length && cnt + words[i].length() + 1 <= maxWidth){ cnt += words[i++].length() + 1; } int space = maxWidth - cnt; int len = i - pos - 1; StringBuilder sb = new StringBuilder(); if (len == 0 || i == words.length){ sb = new StringBuilder(); for (int k = pos; k < i; ++k){ sb.append(words[k] + " "); } sb.deleteCharAt(sb.length()-1); for (int k = 0; k < space; ++k){ sb.append(" "); } pos = i; ans.add(sb.toString()); } else{ sb = new StringBuilder(); if (space % len == 0){ for (int k = pos; k < i; ++k){ sb.append(words[k] + " "); for (int l = 0; l < space / len; ++l){ sb.append(" "); } } pos = i; ans.add(sb.toString().trim()); } else{ int re = space % len; int sp = space / len; for (int k = pos; k < i; ++k){ sb.append(words[k] + " " + ((re != 0) ? " " : "")); re = Math.max(--re,0); for (int l = 0; l < sp; ++l){ sb.append(" "); } } pos = i; ans.add(sb.toString().trim()); } } } return ans; }
其实,space % len 都不需要单独出逻辑来,因为如果re为0就不append,如果re不为0,把这部分加上去就好了。
代码如下:(第四版本)
public List<String> fullJustify(String[] words, int maxWidth) { List<String> ans = new ArrayList<>(); int pos = 0; while (pos < words.length){ int cnt = words[pos].length(); int i = pos + 1; while (i < words.length && cnt + words[i].length() + 1 <= maxWidth){ cnt += words[i++].length() + 1; } int space = maxWidth - cnt; int len = i - pos - 1; StringBuilder sb = new StringBuilder(); if (len == 0 || i == words.length){ sb = new StringBuilder(); for (int k = pos; k < i; ++k){ sb.append(words[k] + " "); } sb.deleteCharAt(sb.length()-1); for (int k = 0; k < space; ++k){ sb.append(" "); } ans.add(sb.toString()); } else{ sb = new StringBuilder(); int re = space % len; int sp = space / len; for (int k = pos; k < i; ++k){ sb.append(words[k] + " " + ((re != 0) ? " " : "")); re = Math.max(--re,0); for (int l = 0; l < sp; ++l){ sb.append(" "); } } ans.add(sb.toString().trim()); } pos = i; } return ans; }
比较满意了。
Leetcode 151. Reverse Words in a String
用自带的java库函数做一把。public String reverseWords(String s) { String[] words = s.trim().split(" +"); Stack<String> stack = new Stack<>(); for (String word : words){ stack.push(word); } StringBuilder sb = new StringBuilder(); while (!stack.isEmpty()){ sb.append(stack.pop() + " "); } return sb.toString().trim(); }
三行版本:
public String reverseWords(String s) { String[] words = s.trim().split(" +"); Collections.reverse(Arrays.asList(words)); return String.join(" ", words); }
相关文章推荐
- 算法细节系列(33):再见字符串(1)
- 算法细节系列(14):动态规划之字符串处理
- C/C++面试之算法系列--如何实现用更少的空间表示英文字母(a ~ z)构成char A[n]字符串
- 算法系列(一)字符串数组的颠倒输出
- 算法系列之四:字符串的相似度
- C# SEO整合系列之字符串相似度算法——Levenshtein Distance method
- 字符串系列算法题
- C/C++面试之算法系列--从“整数转换成字符串”看算法的联想
- [算法系列之十三]Rabin-Karp字符串查找算法
- 算法系列之四:字符串的相似度
- 每天学习一算法系列(34)(求一个二叉树中任意两个节点间的最大距离,两个节点的距离的定义是这两个节点间边的个数)
- 算法系列(一) 删除字符串中的相同字符片段
- C/C++面试之算法系列--寻找最大公共子字符串
- 每天学习一算法系列(1)(定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部)
- C/C++面试之算法系列--从“整数转换成字符串”看算法的联想
- C/C++面试之算法系列--从“整数转换成字符串”看算法的联想
- C/C++面试之算法系列--atoi(char *str)将字符串转换成整数
- C/C++面试之算法系列--atoi(char *str)将字符串转换成整数
- 每天学习一算法系列(20)(输入一个表示整数的字符串,把该字符串转换成整数并输出)
- C/C++面试之算法系列--如何实现用更少的空间表示英文字母(a ~ z)构成char A[n]字符串