745. Prefix and Suffix Search

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.

Solution

Prefix tree and Suffix tree, time O(nk) + O(q * (n + k)), space O(nk)

n: words length
k: max word length
q: f query count

class WordFilter {
    private Trie prefixTrie;
    private Trie suffixTrie;

    public WordFilter(String[] words) {
        prefixTrie = new Trie();
        suffixTrie = new Trie();
        
        for (int i = 0; i < words.length; ++i) {
            String word = words[i];
            // build prefix trie
            Trie curr = prefixTrie;
            for (int j = 0; j < word.length(); ++j) {
                int k = word.charAt(j) - 'a';
                if (curr.children[k] == null) {
                    curr.children[k] = new Trie();
                }
                curr = curr.children[k];
            }
            
            curr.isWord = true;
            curr.weight = i;
            // build suffix trie
            curr = suffixTrie;
            for (int j = word.length() - 1; j >= 0; --j) {
                int k = word.charAt(j) - 'a';
                if (curr.children[k] == null) {
                    curr.children[k] = new Trie();
                }
                curr = curr.children[k];
            }
            
            curr.isWord = true;
            curr.weight = i;
        }
    }
    
    public int f(String prefix, String suffix) {
        PriorityQueue<Integer> prefixQueue = new PriorityQueue<>((a, b) -> (b - a));
        PriorityQueue<Integer> suffixQueue = new PriorityQueue<>((a, b) -> (b - a));
        getWords(prefixTrie, prefix, prefixQueue);
        getWords(suffixTrie, new StringBuilder(suffix).reverse().toString(), suffixQueue);
        // get the largest common element of two priorityQueue
        while (!prefixQueue.isEmpty() && !suffixQueue.isEmpty()) {
            if (prefixQueue.peek() > suffixQueue.peek()) {
                prefixQueue.poll();
            } else if (prefixQueue.peek() < suffixQueue.peek()) {
                suffixQueue.poll();
            } else {
                return prefixQueue.poll();
            }
        }
        
        return -1;
    }
    
    public void getWords(Trie root, String s, PriorityQueue<Integer> queue) {
        for (int i = 0; i < s.length() && root != null; ++i) {
            root = root.children[s.charAt(i) - 'a'];
        }
        
        if (root == null) {
            return;
        }
        
        getWords(root, queue);
    }
    
    public void getWords(Trie root, PriorityQueue<Integer> queue) {
        if (root.isWord) {  // add root first!
            queue.offer(root.weight);
        }
        
        for (int i = 0; i < root.children.length; ++i) {
            if (root.children[i] == null) {
                continue;
            }
            
            getWords(root.children[i], queue);
        }
    }
    
    class Trie {
        Trie[] children;
        int weight;
        boolean isWord;
        
        public Trie() {
            children = new Trie[26];
        }
    }
}

/**
 * Your WordFilter object will be instantiated and called as such:
 * WordFilter obj = new WordFilter(words);
 * int param_1 = obj.f(prefix,suffix);
 */

Two Tries, store information in parent node, time O(nk) + O(q * (n + k)), space O(nk)

由于f函數(shù)會被多次調(diào)用笼裳,每次都找到某個prefix下的所有節(jié)點太耗時了仗颈,我們可以將子節(jié)點的信息存儲在從根到它路徑上的所有父節(jié)點中刻盐,這樣只需要找到prefix到達的那個節(jié)點,就知道它的子節(jié)點的weights了蠢箩。

下面這種寫法竟然TLE也是醉醉的链蕊,理論上應(yīng)該比第一種解法快很多啊...WordFilter的初始化會慢一些(因為增加了set操作)事甜,但是f方法會快,不過看樣子也快不了多少滔韵,因為總的words count就不太多讳侨。

class WordFilter {
    private Trie prefixTrie;
    private Trie suffixTrie;

    public WordFilter(String[] words) {
        prefixTrie = new Trie();
        suffixTrie = new Trie();
        
        for (int i = 0; i < words.length; ++i) {
            String word = words[i];
            // build prefix trie
            Trie curr = prefixTrie;
            curr.weights.add(i);
            for (int j = 0; j < word.length(); ++j) {
                int k = word.charAt(j) - 'a';
                if (curr.children[k] == null) {
                    curr.children[k] = new Trie();
                }
                curr = curr.children[k];
                curr.weights.add(i);
            }
            
            // build suffix trie
            curr = suffixTrie;
            curr.weights.add(i);
            for (int j = word.length() - 1; j >= 0; --j) {
                int k = word.charAt(j) - 'a';
                if (curr.children[k] == null) {
                    curr.children[k] = new Trie();
                }
                curr = curr.children[k];
                curr.weights.add(i);
            }
        }
    }
    
    public int f(String prefix, String suffix) {
        Set<Integer> prefixWeights = findWeights(prefixTrie, prefix);
        Set<Integer> suffixWeights = findWeights(suffixTrie, new StringBuilder(suffix).reverse().toString());
        int maxWeight = -1;
        
        for (int weight : prefixWeights) {
            if (suffixWeights.contains(weight) && maxWeight < weight) {
                maxWeight = weight;
            }
        }
        
        return maxWeight;
    }

    public Set<Integer> findWeights(Trie root, String s) {
        for (int i = 0; i < s.length() && root != null; ++i) {
            root = root.children[s.charAt(i) - 'a'];
        }
        
        return root == null ? Collections.EMPTY_SET : root.weights;
    }
    
    class Trie {
        Trie[] children;
        Set<Integer> weights;
        
        public Trie() {
            children = new Trie[26];
            weights = new HashSet<>();
        }
    }
}

/**
 * Your WordFilter object will be instantiated and called as such:
 * WordFilter obj = new WordFilter(words);
 * int param_1 = obj.f(prefix,suffix);
 */

Trie of Suffix Wrapped Words

Consider the word 'apple'. For each suffix of the word, we could insert that suffix, followed by '#', followed by the word, all into the trie.

For example, we will insert '#apple', 'e#apple', 'le#apple', 'ple#apple', 'pple#apple', 'apple#apple' into the trie. Then for a query like prefix = "ap", suffix = "le", we can find it by querying our trie for le#ap.

Complexity Analysis

Time Complexity: O(NK^2 + QK) where N is the number of words, K is the maximum length of a word, and Q is the number of queries.

Space Complexity: O(NK^2), the size of the trie.

class WordFilter {
    TrieNode trie;
    public WordFilter(String[] words) {
        trie = new TrieNode();
        for (int weight = 0; weight < words.length; ++weight) {
            String word = words[weight] + "{";
            for (int i = 0; i < word.length(); ++i) {
                TrieNode cur = trie;
                cur.weight = weight;
                for (int j = i; j < 2 * word.length() - 1; ++j) {
                    int k = word.charAt(j % word.length()) - 'a';
                    if (cur.children[k] == null)
                        cur.children[k] = new TrieNode();
                    cur = cur.children[k];
                    cur.weight = weight;
                }
            }
        }
    }
    public int f(String prefix, String suffix) {
        TrieNode cur = trie;
        for (char letter: (suffix + '{' + prefix).toCharArray()) {
            if (cur.children[letter - 'a'] == null) return -1;
            cur = cur.children[letter - 'a'];
        }
        return cur.weight;
    }
}

class TrieNode {
    TrieNode[] children;
    int weight;
    public TrieNode() {
        children = new TrieNode[27];
        weight = 0;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市奏属,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌潮峦,老刑警劉巖囱皿,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異忱嘹,居然都是意外死亡嘱腥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門拘悦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來齿兔,“玉大人,你說我怎么就攤上這事础米》治” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵屁桑,是天一觀的道長医寿。 經(jīng)常有香客問我,道長蘑斧,這世上最難降的妖魔是什么靖秩? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮竖瘾,結(jié)果婚禮上沟突,老公的妹妹穿的比我還像新娘。我一直安慰自己捕传,他們只是感情好惠拭,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著庸论,像睡著了一般求橄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葡公,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天罐农,我揣著相機與錄音,去河邊找鬼催什。 笑死涵亏,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播气筋,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼拆内,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宠默?” 一聲冷哼從身側(cè)響起麸恍,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎搀矫,沒想到半個月后抹沪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡瓤球,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年融欧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卦羡。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡噪馏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出绿饵,到底是詐尸還是另有隱情欠肾,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布拟赊,位于F島的核電站董济,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏要门。R本人自食惡果不足惜虏肾,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望欢搜。 院中可真熱鬧封豪,春花似錦、人聲如沸炒瘟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疮装。三九已至缘琅,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廓推,已是汗流浹背刷袍。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留樊展,地道東北人呻纹。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓堆生,卻偏偏與公主長得像,于是被迫代替她去往敵國和親雷酪。 傳聞我的和親對象是個殘疾皇子淑仆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,334評論 0 10
  • **2014真題Directions:Read the following text. Choose the be...
    又是夜半驚坐起閱讀 9,511評論 0 23
  • 細讀這本書,還不到一半哥力,發(fā)現(xiàn)這本書的寫作風(fēng)格類似科學(xué)論文蔗怠,非常嚴(yán)謹(jǐn),詳細吩跋!先提問寞射,舉例,然后佐證钞澳,再結(jié)論,作者...
    羊毛風(fēng)鈴12閱讀 142評論 0 0
  • 圖/文 : 茉莉 那時候 竹子做的劍涨缚,劃過小臉 貓咪最愛將你舔 那一天 爺爺突然反對咱見面 鏡子被我摔成碎片 那一...
    茉莉的小茶館閱讀 285評論 13 7
  • 哈嘍脓魏,大家好兰吟,今天的笑笑再次和大家見面了。 今天訓(xùn)練營的演講課題是夢想茂翔。 你有夢想嗎混蔼?你的夢想是什么?如果我問一個...
    笑莉說閱讀 229評論 10 13