Jerry's Blog

Recording what I learned everyday

View on GitHub


11 November 2019

LeetCode(63) -- 20, 138, 139

by Jerry Zhang

P20. Valid Parentheses (Easy))

Given a string containing just the characters ‘(‘, ‘)’, ‘{‘, ‘}’, ‘[’ and ‘]’, determine if the input string is valid.

我的思路

用三个整数来记录开闭括号的次数。

我的代码

public class E_20_ValidParentheses {
    public boolean isValid(String s) {
        char[] stack = new char[s.length()];
        char[] chars = s.toCharArray();
        int j = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '(' || chars[i] == '[' || chars[i] == '{') {
                stack[j++] = chars[i];
            } else {
                if (j == 0 || stack[j - 1] != getOpen(chars[i])) {
                    return false;
                } else {
                    j--;
                }
            }
        }
        return j == 0;
    }

    private char getOpen(char c) {
        if (c == ')') return '(';
        if (c == ']') return '[';
        if (c == '}') return '{';
        return 0;
    }
}

100%

P138. Copy List with Random Pointer (Medium)

A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.

Return a deep copy of the list.

讨论区的思路

遍历这个list三次。第一次,复制每个节点(及其value),并且把它放到原节点的右边; 第二次,把原节点的random的指针的下一个,赋给新节点的random; 第三次,把新生成的这些节点连接到一个新的list中,并与原list脱钩。

最优解

public class M_138_CopyListwithRandomPointer {
    public Node copyRandomList(Node head) {
        Node curr = head;
        while (curr != null) {
            Node copy = new Node();
            copy.val = curr.val;
            copy.next = curr.next;
            curr.next = copy;
            curr = curr.next.next;
        }
        curr = head;

        while (curr != null) {
            if (curr.random != null) {
                curr.next.random = curr.random.next;
            }
            curr = curr.next.next;
        }
        curr = head;
        Node ans = new Node();
        Node copyCurr = ans;
        while (curr != null) {
            copyCurr.next = curr.next;
            copyCurr = copyCurr.next;
            curr.next = curr.next.next;
            curr = curr.next;
        }
        return ans.next;
    }
}

100%

P139. Word Break (Medium)

给一个长单词和一个字典,问这个长单词能否拆分成字典里的词。

我的思路

首先想到用Trie来记录一下字典里的单词。问题的关键在于,如果遇到一个完整的单词后,到底要不要拆。

Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
Output: false

比如这种情况,遇到cat后要不要拆?还是在cats拆?然后感觉像是DFS了,没思路了。

讨论区

public class M_139_WordBreak {
    public boolean wordBreak(String s, List<String> wordDict) {
        HashSet<String> dict = new HashSet<>(wordDict);
        boolean[] word = new boolean[s.length() + 1];
        word[0] = true;

        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (word[j] && dict.contains(s.substring(j, i))) {
                    word[i] = true;
                    break;
                }
            }
        }
        return word[s.length()];
    }
}

58%,就是把所有的字母的两两组合全试一遍,只要前面成功了,就只看新加的部分就行了。

最优解

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        boolean bad[] = new boolean[s.length()];
        return wordBreak(s, 0, wordDict, bad);
    }

    public boolean wordBreak(String s, int offset, List<String> wordDict, boolean[] bad){
        if(s.length() == offset ){
            return true;
        }
        if(bad[offset]) return false;
        for(String dict: wordDict){
            if(s.startsWith(dict, offset)){
                if(wordBreak(s, offset + dict.length(), wordDict, bad)) return true;
            }
        }
        bad[offset] = true;
        return false;
    }
}

用了memo来存已经访问过的不成功的记录。这个方法一大亮点是用dict来遍历,而不是遍历长字符串。另外有一个startsWith(word, offset)方法值得学习,是从某个index开始,看是不是以某个字符串开头。这个方法在解析字符串时,可以省去不少操作。

tags: LeetCode