leetcode 820

这题一开始我没完全理解题意,以为最终的结果里原先的元素要保持原本顺序,其实不是。

还有一点理解错了,我以为如果s1的后缀和s2的前缀有重合的话,s1和s2可以组成一个更长的字符串,中间部分是他们共享的,其实理解错了,比如对于 abc 和 bcd,如果按照我的理解,变成了abcd#,然后题目要求的是,对于每一个#,都要找到一个位置i,从i到这个#的字符串是原words数组的一个元素,那么对于abcd#,无论如何找不到一个i使得i到#组成abc。

所以这题要的效果是如果s1是s2的后缀的一部分,那么最终得到的结果字符串里只包含s2,不好含s1。比如abc和bc,应该组成abc#,这样i分别取0和1,再以#为右边界,就能得到abc和bc。还有一点,对于最终的结果,是分成很多组的,每一组都是由有相同后缀的字符串合并出来的,注意被合并的字符串应当整体是另一个更长字符串的后缀。比如abcde和de组成abcde#,而abcde和eg就不行,就只能变成abcde#eg#。

终于搞清楚题意之后就可以做了。

第一种方法是暴力,先把所有字符串放到一个set里,因为要去重,然后对于每个字符串,从下标1开始依次去取子串,然后尝试在set里删除。比如对于abcde,依次在set中尝试删除bcde、cde、de、e。最终剩下的set里每个元素后面加上#,然后随意顺序组合起来就是要的答案。

class Solution {
    public int minimumLengthEncoding(String[] words) {
        Set<String> set = new HashSet<>(Arrays.asList(words));
        for (String w : words) {
            for (int i = 1; i < w.length(); i++) {
                set.remove(w.substring(i));
            }
        }
        int cnt = 0;
        for (String s : set) {
            cnt += s.length() + 1;
        }
        return cnt;
    }
}

另一种做法使用Trie树,字典树,正好复习一下这个数据结构,属于是空间换时间,利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

注意到,如果两个字符串有公共前缀的话,那么应该保存在Trie同一条路径里。而这题要的是整体是另个字符串后缀的字符串,那么可以把原始字符串全部反过来,然后存在Trie里,再去遍历每个字符串,遍历到最后一个字母时,判断是不是叶子节点,如果是的话,说明这个字符串应该添加到结果里。举例子,time和me,反过来之后时emit和em,显然它们存在一个路径下面,遍历em到m时发现m还有叶子节点,说明em反过来的话必定是其他字符串的后缀,所以me不能加到结果里。

至于怎么判断一个节点是不是叶子节点,因为每一个TrieNode都有一个TrieNode[] next结构,并且这个数组在创建TrieNode时就创建了,但是数组里每个元素默认都是null,所以就遍历这个next数组,只要有一位不是null说明就不是叶子节点。

class Solution {
    public int minimumLengthEncoding(String[] words) {
        Set<String> reverseWords = new HashSet<>();
        for (String w : words) {
            reverseWords.add(new StringBuilder(w).reverse().toString());
        }
        Trie trie = new Trie();
        for (String w : reverseWords) {
            trie.insert(w);
        }
        int cnt = 0;
        for (String w : reverseWords) {
            if (trie.isLeaf(trie.travel(w))) {
                cnt += w.length() + 1;
            }
        }
        return cnt;
    }
}

class Trie {
    TrieNode root;

    public Trie() {
        root = new TrieNode(-1);
    }

    public void insert(String s) {
        TrieNode cur = root;
        for (int i = 0; i < s.length(); i++) {
            int c = s.charAt(i) - 'a';
            if (cur.next[c] == null) {
                cur.next[c] = new TrieNode(c);
            }
            cur = cur.next[c];
        }
        cur.isEnd = true;
    }

    public TrieNode travel(String s) {
        TrieNode cur = root;
        for (int i = 0; i < s.length(); i++) {
            int c = s.charAt(i) - 'a';
            if (cur.next[c] == null) {
                return null;
            }
            cur = cur.next[c];
        }
        return cur;
    }

    public boolean isLeaf(TrieNode t) {
        for (int i = 0; i < t.next.length; i++) {
            if (t.next[i] != null) {
                return false;
            }
        }
        return true;
    }

    public boolean search(String s) {
        TrieNode cur = root;
        for (int i = 0; i < s.length(); i++) {
            int c = s.charAt(i) - 'a';
            if (cur.next[c] == null) {
                return false;
            }
            cur = cur.next[c];
        }
        return cur.isEnd;
    }


}

class TrieNode {
    int val;

    public TrieNode(int v) {
        val = v;
    }

    boolean isEnd;
    TrieNode[] next = new TrieNode[26];
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值