820.单词的压缩编码

84 阅读1分钟

题目:
单词数组 words 的 有效编码 由任意助记字符串 s 和下标数组 indices 组成,且满足:

  • words.length == indices.length
  • 助记字符串 s 以 '#' 字符结尾
  • 对于每个下标 indices[i] ,s 的一个从 indices[i] 开始、到下一个 '#' 字符结束(但不包括 '#')的 子字符串 恰好与 words[i] 相等

给你一个单词数组 words ,返回成功对 words 进行编码的最小助记字符串 s 的长度 。
算法:

方法一:存储后缀
读懂题意很重要,1.indices数组是可以任意指定的;2.indices[i]到#之前的子字符串与word[i]相等,a是b的后缀相等则可以编码到一起。那么我们可以尽可能把短的字符串尝试匹配长的字符串的后缀。如果能匹配上,就把短的字符串删掉。

func minimumLengthEncoding(words []string) int {
	set := make(map[string]struct{})
	for i := 0; i < len(words); i++ {
		set[words[i]] = struct{}{}
	}
	for i := 0; i < len(words); i++ {
		for j := 1; j < len(words[i]); j++ {
			if _, ok := set[words[i][j:]]; ok {
				delete(set, words[i][j:])
			}
		}
	}
	ans := 0
	for word := range set {
		ans = ans + len(word) + 1
	}
	return ans
}

方法二:tier字典树
将words的每个字符串反序插入字典树,看插入了多少的字符串

func minimumLengthEncoding(words []string) int {
	tier := NewTier()
	nodeWordIndexMap := make(map[*Tier]int)
	// 遍历words,对word逆序构建tier树
	for i := range words {
		cur := tier
		for j := len(words[i]) - 1; j >= 0 ;j -- {
			cur = cur.Get(int(words[i][j] - 'a'))
		}
                // 这里很巧妙,假设生成e-m-i-t-leaf,此时cur=leaf,leaf.Count=0 ,如果继续生成了e-m,则leaf节点=i,count=1,不是叶子节点,所以以此来用 if node.Count == 0判断是否叶子节点。
		nodeWordIndexMap[cur] = i
	}
	// 遍历tier数组,如果是叶子节点,则ans = ans + len(word) + 1
	ans := 0
	for node, index := range nodeWordIndexMap {
		if node.Count == 0 {
			ans = ans + len(words[index]) + 1
		}
		
	}
	return ans
}

// tier树
type Tier struct{
	Nodes []*Tier
	Count int
}

func NewTier() *Tier {
	return &Tier{make([]*Tier, 26), 0}
}

func (t *Tier) Get(index int) *Tier {
	if t.Nodes[index] == nil {
		t.Nodes[index] = NewTier()
		t.Count ++
	}
	return t.Nodes[index] 
}