算法细节系列(34):再见字符串(2)
来源:互联网 发布:ff14人族捏脸数据分享 编辑:程序博客网 时间:2024/05/22 01:36
算法细节系列(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);}
阅读全文
0 0
- 算法细节系列(34):再见字符串(2)
- 算法细节系列(33):再见字符串(1)
- 算法细节系列(14):动态规划之字符串处理
- 算法细节系列(2):231.Power of Two && Three
- 算法细节系列(1):Java swap
- 算法细节系列(12):破除想当然
- 算法细节系列(13):买卖股票
- 算法细节系列(21):贪心有理?
- 算法细节系列(23):回溯
- 算法细节系列(25):加减乘除
- 算法细节系列(26):区间
- 算法细节系列(28):线段树
- 算法细节系列(29):any sum
- 算法细节系列(30):接口设计
- 算法细节系列(31):链表
- 算法细节系列(15):Valid Parentheses系列
- 算法细节系列(20):Word Ladder系列
- 算法细节系列(4):二分查找总结
- SAPUI5教程——ABAP环境下SAP Fiori 系统搭建以及开发实践
- spring和spring mvc 中有关父子容器自我归纳
- AWS EC2小结
- oracle学习使用--oracle安装都安装为企业版
- 请问js对象属性值为什么用数组也可以访问
- 算法细节系列(34):再见字符串(2)
- 151组最易混淆的单词
- 二维数组中的查找(java版)
- Mysql有两种存储引擎:InnoDB与Myisam
- Java中序列化的serialVersionUID作用
- 一个SQLiteReadOnlyDatabaseException的问题
- Android中的消息队列和线程队列机制
- 设置Dialog的屏幕位置、背景等
- 领域驱动设计系列文章(1)——通过现实例子显示领域驱动设计的威力