2022-05-14:语法补全功能,比如"as soon as possible", 当我们识别到"as soon as"时, 基本即可判定用户需要键入"possible"。 设计一个统计词频的模型,用于这个功能, 类似(prefix, next word)这样的二元组, 比如一个上面的句子"as soon as possible", 有产生如下的二元组(as, soon, 1)、(as soon, as, 1)、(as soon as, possible, 1)。 意思是这一个句子产生了如下的统计: 当前缀为"as",接下来的单词是"soon",有了1个期望点; 当前缀为"as soon",接下来的单词是"as",有了1个期望点; 当前缀为"as soon as",接下来的单词是"possible",有了1个期望点。 那么如果给你很多的句子,当然就可以产生很多的期望点,同一个前缀下,同一个next word的期望点可以累加。 现在给你n个句子,让你来建立统计, 然后给你m个句子,作为查询, 最后给你k,表示每个句子作为前缀的情况下,词频排在前k名的联想。 返回m个结果,每个结果最多k个单词。 来自字节飞书团队。

答案2022-05-14:

前缀树。本来想用rust编写,但实力有限,实在写不出。所以用go语言了。

代码用golang编写。代码如下:

package main

import (
	"fmt"
	"sort"
	"strings"
)

func main() {
	sentences := []string{"i think you are good", "i think you are fine", "i think you are good man"}
	k := 2
	ai := NewAI(sentences, k)
	for _, ans := range ai.suggest("i think you are") {
		fmt.Println(ans)
	}
	fmt.Println("=====")
	ai.fill("i think you are ***ing good")
	ai.fill("i think you are ***ing great")
	ai.fill("i think you are ***ing genius")
	for _, ans := range ai.suggest("i think you are") {
		fmt.Println(ans)
	}
	fmt.Println("=====")
}

type TrieNode struct {
	word      string
	times     int
	nextNodes map[string]*TrieNode
	nextRanks []*TrieNode
}

func NewTrieNode(w string) *TrieNode {
	ans := &TrieNode{}
	ans.word = w
	ans.times = 1
	ans.nextNodes = make(map[string]*TrieNode)
	ans.nextRanks = make([]*TrieNode, 0)
	return ans
}

type AI struct {
	root *TrieNode
	topk int
}

func NewAI(sentences []string, k int) *AI {
	ans := &AI{}
	ans.root = NewTrieNode("")
	ans.topk = k
	for _, sentence := range sentences {
		ans.fill(sentence)
	}
	return ans
}

func (this *AI) fill(sentence string) {
	cur := this.root
	var next *TrieNode
	for _, word := range strings.Split(sentence, " ") {
		if _, ok := cur.nextNodes[word]; !ok {
			next = NewTrieNode(word)
			cur.nextNodes[word] = next
			cur.nextRanks = append(cur.nextRanks, next)
		} else {
			next = cur.nextNodes[word]
			next.times++
		}
		cur = next
	}
}

func (this *AI) suggest(sentence string) []string {
	ans := make([]string, 0)
	cur := this.root
	for _, word := range strings.Split(sentence, " ") {
		if _, ok := cur.nextNodes[word]; !ok {
			return ans
		} else {
			cur = cur.nextNodes[word]
		}
	}
	sort.Slice(cur.nextRanks, func(i, j int) bool {
		a := cur.nextRanks[i]
		b := cur.nextRanks[j]
		if a.times != b.times {
			return a.times > b.times
		} else {
			return a.word < b.word
		}
	})
	for _, n := range cur.nextRanks {
		ans = append(ans, n.word)
		if len(ans) == this.topk {
			break
		}
	}
	return ans
}

执行结果如下:

在这里插入图片描述


左神java代码