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

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

作者头像
福大大架构师每日一题
发布2023-06-08 16:56:07
3040
发布2023-06-08 16:56:07
举报

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代码如下:

代码语言:javascript
复制
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代码如下:

代码语言:javascript
复制
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)
}
本文参与?腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-04-17,如有侵权请联系?cloudcommunity@tencent.com 删除

本文分享自 福大大架构师每日一题 微信公众号,前往查看

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

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com