您的位置:首页 > 其它

算法细节系列(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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐