前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >2023-04-17:设计一个包含一些单词的特殊词典,并能够通过前缀和后缀来检索单词。 实现 WordFilter 类: WordFilter(string[]

2023-04-17:设计一个包含一些单词的特殊词典,并能够通过前缀和后缀来检索单词。 实现 WordFilter 类: WordFilter(string[]

原创
作者头像
福大大架构师每日一题
发布于 2023-04-17 14:16:50
发布于 2023-04-17 14:16:50
3890
举报

2023-04-17:设计一个包含一些单词的特殊词典,并能够通过前缀和后缀来检索单词。

实现 WordFilter 类:

WordFilter(string[] words) 使用词典中的单词 words 初始化对象

f(string pref, string suff)

返回词典中具有前缀 prefix 和后缀 suff 的单词的下标

如果存在不止一个满足要求的下标,返回其中 最大的下标

如果不存在这样的单词,返回 -1 。

输入:

"WordFilter", "f"

[["apple"], "a", "e"]。

输出:

null, 0。

答案2023-04-17:

大体过程如下:

1.首先定义一个 Trie 树的结点类型 TrieNode,包含 nexts 数组和 indies 切片,其中 nexts 数组用于存储子节点,indies 切片用于存储当前节点对应的单词在原单词数组中的下标。

2.然后定义 WordFilter 结构体,包含两个指向 Trie 树根节点的指针,分别用于存储正序和倒序的 Trie 树。

3.实现 Constructor 方法,接受一个字符串数组作为参数,初始化 WordFilter 对象。在该方法内部,遍历单词数组,将每个单词插入正序和倒序的 Trie 树中。

4.实现 F 方法,接受两个字符串作为前缀和后缀参数,查找并返回满足要求的单词在原单词数组中的下标。该方法内部,分别在正序和倒序 Trie 树上匹配前缀和后缀,获取包含相应前缀和后缀的单词的下标集合。然后遍历较短的下标集合,依次在较长的下标集合中二分查找,找到最大的匹配下标。如果没有找到任何匹配,返回 -1。

5.在主函数中创建 WordFilter 对象,调用 F 方法,输出结果。

时间复杂度:

  • 构造函数 Constructor 的时间复杂度为 $O(NL^2)$,其中 $N$ 是单词数组的长度,$L$ 是单词的最大长度。
  • 查找函数 F 的时间复杂度为 $O(M \log N)$,其中 $M$ 是相应前缀和后缀所匹配到的下标集合的大小,$N$ 是单词数组的长度。

空间复杂度:

  • 构造函数 Constructor 的空间复杂度为 $O(NL^2)$,即正序和倒序两棵 Trie 树的总节点数。
  • 查找函数 F 的空间复杂度为 $O(1)$,只需要常量级别的空间存储中间变量。

golang代码如下:

代码语言:go
AI代码解释
复制
package main

import "fmt"

type TrieNode struct {
	nexts  [26]*TrieNode
	indies []int
}

func NewTrieNode() *TrieNode {
	return &TrieNode{
		nexts:  [26]*TrieNode{},
		indies: []int{},
	}
}

type WordFilter struct {
	preHead *TrieNode
	sufHead *TrieNode
}

func Constructor(words []string) WordFilter {
	wf := WordFilter{
		preHead: NewTrieNode(),
		sufHead: NewTrieNode(),
	}
	for i, word := range words {
		cur := wf.preHead
		for _, c := range word {
			path := c - 'a'
			if cur.nexts[path] == nil {
				cur.nexts[path] = NewTrieNode()
			}
			cur = cur.nexts[path]
			cur.indies = append(cur.indies, i)
		}
		cur = wf.sufHead
		for j := len(word) - 1; j >= 0; j-- {
			path := word[j] - 'a'
			if cur.nexts[path] == nil {
				cur.nexts[path] = NewTrieNode()
			}
			cur = cur.nexts[path]
			cur.indies = append(cur.indies, i)
		}
	}
	return wf
}

func (this *WordFilter) F(pref string, suff string) int {
	var preList, sufList []int
	cur := this.preHead
	for i := 0; i < len(pref) && cur != nil; i++ {
		cur = cur.nexts[pref[i]-'a']
	}
	if cur != nil {
		preList = cur.indies
	}
	if preList == nil {
		return -1
	}
	cur = this.sufHead
	for i := len(suff) - 1; i >= 0 && cur != nil; i-- {
		cur = cur.nexts[suff[i]-'a']
	}
	if cur != nil {
		sufList = cur.indies
	}
	if sufList == nil {
		return -1
	}
	small, big := preList, sufList
	if len(preList) > len(sufList) {
		small, big = sufList, preList
	}
	for i := len(small) - 1; i >= 0; i-- {
		if bs(big, small[i]) {
			return small[i]
		}
	}
	return -1
}

func bs(sorted []int, num int) bool {
	l, r := 0, len(sorted)-1
	for l <= r {
		m := (l + r) / 2
		midValue := sorted[m]
		if midValue == num {
			return true
		} else if midValue < num {
			l = m + 1
		} else {
			r = m - 1
		}
	}
	return false
}

func main() {
	wordFilter := Constructor([]string{"apple"})
	ans := wordFilter.F("a", "e")
	fmt.Println(ans)
}

rust代码如下:

代码语言:rust
AI代码解释
复制
use std::collections::HashMap;
struct WordFilter {
    trie: Trie,
}

impl WordFilter {
    fn new(words: Vec<String>) -> Self {
        let mut trie = Trie::new();
        for (i, word) in words.iter().enumerate() {
            let w = word.to_string() + "#" + word;
            for j in 0..word.len() {
                let mut node = &mut trie;
                for c in w[j..].chars() {
                    node = node.children.entry(c).or_insert(Trie::new());
                    node.weight = i as i32;
                }
            }
        }
        Self { trie }
    }

    fn f(&self, pref: String, suff: String) -> i32 {
        let k = suff + "#" + &pref;
        let mut node = &self.trie;
        for c in k.chars() {
            if let Some(child) = node.children.get(&c) {
                node = child;
            } else {
                return -1;
            }
        }
        node.weight
    }
}

struct Trie {
    children: HashMap<char, Trie>,
    weight: i32,
}

impl Trie {
    fn new() -> Self {
        Self {
            children: HashMap::new(),
            weight: 0,
        }
    }
}

fn main() {
    let mut wordFilter = WordFilter::new(vec![String::from("apple")]);
    let ans = wordFilter.f(String::from("a"), String::from("e"));
    println!("ans = {}", ans)
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 大体过程如下:
  • 时间复杂度:
  • 空间复杂度:
  • golang代码如下:
  • rust代码如下:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档