71. Simplify Path

用双端队列比 stack 省事很多。收尾的时候注意下,如果字符串为空的话,需要留个 "/" 作为默认情况。

06/19更新:这道题和LC224有一个细节是一样的,就是对于空字符串的处理,很显然你要考虑跳过空字符串,不然会出错,但我觉得这个细节很神奇在于这些空字符串是怎么来的呢?

public class Solution {
    public String simplifyPath(String path) {
        Deque<String> queue = new LinkedList<>();
        String[] array = path.split("/");
        for (String str : array) {
            if (str.equals(".") || str.equals("")) continue;
            if (str.equals("..")) queue.pollFirst();
            else queue.offerFirst(str);
        }
        StringBuilder sb = new StringBuilder();
        while (!queue.isEmpty()) {
            sb.append("/");
            sb.append(queue.pollLast());
        }
        return sb.length() == 0 ? "/" : sb.toString();
    }
}

14. Longest Common Prefix

没什么值得一提的算法思路,无脑比较就行了。prefix越来越短

public class Solution {
    public String longestCommonPrefix(String[] strs) {
        if (strs == null || strs.length == 0) return "";
        String prefix = strs[0];
        for (int i = 1; i < strs.length; ++i) {
            int j = 0;
            while (j < strs[i].length() && j < prefix.length() && strs[i].charAt(j) == prefix.charAt(j)) {
                j++;
            }
            if (j == 0) return "";
            prefix = prefix.substring(0, j);
        }
        return prefix;
    }
}

394. Decode String / Expression Expand

步骤上不麻烦,遇到嵌套的括号,就把原始 String 变成更小的子问题,递归处理就好了。于是这题操作上总共就三件事:

  • 一边扫一边记录当前数字,times 初始化 = 0;

  • 找到当前括号的匹配括号;

  • 括号之间就是一个新的子问题,递归处理之后把结果用循环添加就好了。

public class Solution {
    public String decodeString(String s) {
        if(s == null || s.length() == 0) return "";
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            if (Character.isDigit(ch)) {
                int times = 0;
                while (i < s.length() && s.charAt(i) != '[') {
                    times = times * 10 + (s.charAt(i) - '0');
                    i++;
                }
                // FIND THE INDEX OF PARENTHESE ']' THAT MATCHES CURRENT '['
                int matchIndex = findIndex(s, i);
                String repeat = decodeString(s.substring(i + 1, matchIndex));

                while (times != 0) {
                    sb.append(repeat);
                    times--;
                }
                i = matchIndex;
            } else {
                sb.append(ch); 
            } 
        }
        return sb.toString();
    }
    private int findIndex(String s, int index) {
        int count = 0;
        for (;index < s.length(); ++index) {
            if (s.charAt(index) == '[') count++;
            if (s.charAt(index) == ']') count--;
            if (count == 0) break;
        }
        return index;
    }
}

161. One Edit Distance

挺有意思,这个解法应该叫捏软柿子法。

public class Solution {
    public boolean isOneEditDistance(String s, String t) {
        if (s.length() > t.length()) {
            String tmp = s;
            s = t;
            t = tmp;
        }
        int diff = t.length() - s.length();
        if (diff > 1) return false;

        if (diff == 0) {
            int count = 0;
            for (int i = 0; i < s.length(); ++i) {
                if (s.charAt(i) != t.charAt(i)) count++;
            }
            return count == 1;
        }
        if (diff == 1) {
            for (int i = 0; i < s.length(); ++i) {
                if (s.charAt(i) != t.charAt(i)) {
                    return s.substring(i).equals(t.substring(i + 1));
                }
            }
        }
        return true;
    }
}

核心思想是,既然有且只有 1 个位置 mismatch,我们可以直接在找到 mismatch 位置上判断:

  • 让 n, m 为两个 String 的长度

  • 如果 m == n ,mismatch 之后的子字符串一定完全相等;

  • 否则长的那段在 i 之后的 substring 一定与短的以 i 开始的 substring 全等;

  • 如果在循环末尾还没有发现 mismatch,两个字符串末尾必然会有 1 的长度差,否则 S == T.

在字符串 DP 中,原始字符串上的 substring,等价于原始区间内的 subproblem。

public class Solution {
    public boolean isOneEditDistance(String s, String t) {
        int n = s.length();
        int m = t.length();

        if(Math.abs(n - m) > 1) return false;

        for(int i = 0; i < Math.min(m,n); i++){
            if(s.charAt(i) != t.charAt(i)){
                if(n == m) return s.substring(i + 1).equals(t.substring(i + 1));
                if(n > m) return s.substring(i + 1).equals(t.substring(i));
                if(n < m) return s.substring(i).equals(t.substring(i + 1));
            }
        }

        return Math.abs(n - m) == 1;
    }
}

402. Remove K Digits

除去k个数,结果要尽量保持小。

思路是,遍历string里的每个数,将结果存进result,如果当前数比result里的数要小,就替换,保留这个较小的数。用k来计数,每次除掉一个k就减1,直到k等于0。

public class Solution {
    public String removeKdigits(String num, int k) {
        int digits = num.length() - k;
        char[] result = new char[num.length()];
        int top = 0;
        for (int i = 0; i < num.length(); ++i) {
            char c = num.charAt(i);
            while (top > 0 && result[top-1] > c && k > 0) {
                top -= 1;
                k -= 1;
            }
            result[top++] = c;
        }
        // find the index of first non-zero digit
        int idx = 0;
        while (idx < digits && result[idx] == '0') idx++;
        return idx == digits? "0": new String(result, idx, digits - idx);
    }
}

well,这题的corner case真是多到自杀。总觉得在哪看过类似的题,就是想不起来,这道题应该到DP或者数组的时候,还有股票问题,那个时候会有很多类似的题,目前就先用stack做这个string题,随便做一下就算了,以后再研究。

06/16更新:if (num.length() == k) return "0"; corner case很多,第一句话处理corner case这个很重要。

public class Solution {
    public String removeKdigits(String num, int k) {
        if (num == null || num.length() == 0) return "";
        if (num.length() == k) return "0";

        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < num.length(); ++i) {
            int curt = num.charAt(i) - '0';

            while (!stack.isEmpty() && k > 0 && curt < stack.peek()) {
                stack.pop();
                k--;
            }
            stack.push(curt);
        }
        while (k > 0) {
            stack.pop();
            k--;
        }
        StringBuilder sb = new StringBuilder();
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
        }
        sb.reverse();
        while (sb.length() > 1 && sb.charAt(0) == '0') sb.deleteCharAt(0);
        return sb.toString();
    }
}

6. ZigZag Conversion

public class Solution {
    public String convert(String s, int numRows) {
        char[] c = s.toCharArray();
        StringBuffer[] sb = new StringBuffer[numRows];
        for (int i = 0; i < numRows; ++i) {
            sb[i] = new StringBuffer();
        }
        int index = 0;
        while (index < s.length()) {
            for (int i = 0; i < numRows && index < s.length(); ++i) {
                sb[i].append(c[index++]);
            }
            for (int i = numRows - 2; i >= 1 && index < s.length(); --i) {
                sb[i].append(c[index++]);
            }
        }
        for (int i = 1; i < numRows; ++i) {
            sb[0].append(sb[i]);
        }
        return sb[0].toString();
    }
}

38. Count and Say

public class Solution {
    public String countAndSay(int n) {
        if (n == 0) return "";
        if (n == 1) return "1";

        String s = "1";

        while (--n > 0) {
            char[] c = s.toCharArray();
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < c.length; ++i) {
                int count = 1;
                while (i + 1 < c.length && c[i] == c[i + 1]) {
                    count++;
                    i++;
                }
                sb.append(String.valueOf(count));
                sb.append(String.valueOf(c[i]));
            }
            s = sb.toString();
        }
        return s;
    }
}

results matching ""

    No results matching ""