745-前缀和后缀搜索

Description

Given many words, words[i] has weight i.

Design a class WordFilter that supports one function, WordFilter.f(String prefix, String suffix). It will return the word with given prefix and suffix with maximum weight. If no word exists, return -1.


Examples:

Input:
WordFilter(["apple"])
WordFilter.f("a", "e") // returns 0
WordFilter.f("b", "") // returns -1

Note:

  1. words has length in range [1, 15000].
  2. For each test case, up to words.length queries WordFilter.f may be made.
  3. words[i] has length in range [1, 10].
  4. prefix, suffix have lengths in range [0, 10].
  5. words[i] and prefix, suffix queries consist of lowercase letters only.

问题描述

给定字符串数组words,words[i]的权重为i。
设计WordsFilter类,支持WordFilter.f(String prefix, String suffix)。
这个函数返回以prefix为前缀,以suffix为后缀的有着最大权重的字符串。
如果没有这样的字符串,那么返回-1。


问题分析

我的做法是通过前缀字典树和后缀字典树找出满足的字符串集合,然后逆序遍历words,判断words[i]是否在字符串集合中(复杂且效率不高)

这个链接记录了另外三种简单的解法,感兴趣的话可以去看看
https://leetcode.com/problems/prefix-and-suffix-search/discuss/110044/Three-ways-to-solve-this-problem-in-Java


解法1(字典树)

class WordFilter {
    //前缀字典树节点
    private class ptNode{
        //拥有该路径为前缀的字符串
        boolean[] prefix;
        ptNode[] children;

        public ptNode(int n){
            prefix = new boolean[n];
            children = new ptNode[26];
        }
    }
    //后缀字典树节点
    private class stNode{
        //拥有该路径为后缀的字符串
        boolean[] suffix;
        stNode[] children;

        public stNode(int n){
            suffix = new boolean[n];
            children = new stNode[26];
        }
    }
    private ptNode proot;
    private stNode sroot;
    private String[] words;
    private int n;
    public WordFilter(String[] words) {
        n = words.length;

        proot = new ptNode(n);
        sroot = new stNode(n);
        this.words = words;
        //遍历words,构造前缀字典树和后缀字典树
        for(int i = 0;i < n;i++){
            addp(proot, words[i], i);
            adds(sroot, words[i], i);
        }
    }

    public int f(String prefix, String suffix) {
        ptNode workNode1 = proot;
        stNode workNode2 = sroot;
        //通过两个循环找出满足指定前缀和后缀的字符串集合
        for(char c : prefix.toCharArray()){
            workNode1 = workNode1.children[c - 'a'];
            if(workNode1 == null)   return -1;
        }

        int i = suffix.length() - 1;
        while(i >= 0){
            workNode2 = workNode2.children[suffix.charAt(i) - 'a'];
            if(workNode2 == null)   return -1;
            i--;
        }
        //逆序遍历words,返回满足条件的字符串
        for(i = n - 1;i >= 0;i--){
            if(workNode1.prefix[i] && workNode2.suffix[i])  return i;
        }

        return -1;
    }
    //构造前缀字典树
    public void addp(ptNode root, String str, int index){
        for(char c : str.toCharArray()){
            //在这里维护满足前缀的字符串数组
            root.prefix[index] = true;
            if(root.children[c - 'a'] == null)  root.children[c - 'a'] = new ptNode(n);
            root = root.children[c - 'a'];
        }
        //还有这里
        root.prefix[index] = true;
    }
    //构造后缀字典树
    public void adds(stNode root, String str, int index){
        int i = str.length() - 1;
        char[] cArr = str.toCharArray();

        while(i >= 0){
            //在这里维护满足后缀的字符串数组
            root.suffix[index] = true;
            if(root.children[cArr[i] - 'a'] == null)  root.children[cArr[i] - 'a'] = new stNode(n);
            root = root.children[cArr[i] - 'a'];
            i--;
        }
        //还有这里
        root.suffix[index] = true;
    }
}

另外两种简单的做法

解法2

class WordFilter {
    //存储满足前缀的权重列表
    HashMap<String, List<Integer>> mapPrefix = new HashMap<>();
    //存储满足后缀的权重列表
    HashMap<String, List<Integer>> mapSuffix = new HashMap<>();

    public WordFilter(String[] words) {
        //循环构建mapPrefix和mapSuffix
        for(int w = 0; w < words.length; w++){
            for(int i = 0; i <= words[w].length(); i++){
                String s = words[w].substring(0, i);
                if(!mapPrefix.containsKey(s)) mapPrefix.put(s, new ArrayList<>());
                mapPrefix.get(s).add(w);
            }
            for(int i = 0; i <= words[w].length(); i++){
                String s = words[w].substring(words[w].length() - i);
                if(!mapSuffix.containsKey(s)) mapSuffix.put(s, new ArrayList<>());
                mapSuffix.get(s).add(w);
            }
        }
    }
    public int f(String prefix, String suffix) {

        if(!mapPrefix.containsKey(prefix) || !mapSuffix.containsKey(suffix)) return -1;
        //得出prefix的权重列表
        List<Integer> p = mapPrefix.get(prefix);
        //得出suffix的权重列表
        List<Integer> s = mapSuffix.get(suffix);
        int i = p.size()-1, j = s.size()-1;
        while(i >= 0 && j >= 0){
            if(p.get(i) < s.get(j)) j--;
            else if(p.get(i) > s.get(j)) i--;
            //若相等,说明找到,直接返回
            else return p.get(i);
        }
        return -1;
    }
}

解法3

class WordFilter {
    HashMap<String, Integer> map = new HashMap<>();

    public WordFilter(String[] words) {
        for(int w = 0; w < words.length; w++){
            for(int i = 0; i <= words[w].length(); i++){
                for(int j = 0; j <= words[w].length(); j++){
                    map.put(words[w].substring(0, i) + "#" + words[w].substring(words[w].length() - j), w);
                }
            }
        }
    }

    public int f(String prefix, String suffix) {
        return (map.containsKey(prefix + "#" + suffix)) ? map.get(prefix + "#" + suffix) : -1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值