面试题 17.13. 恢复空格: dp+字典树

哦,不!你不小心把一个长篇文章中的空格、标点都删掉了,并且大写也弄成了小写。像句子"I reset the computer. It still didn’t boot!"已经变成了"iresetthecomputeritstilldidntboot"。在处理标点符号和大小写之前,你得先把它断成词语。当然了,你有一本厚厚的词典dictionary,不过,有些词没在词典里。假设文章用sentence表示,设计一个算法,把文章断开,要求未识别的字符最少,返回未识别的字符数。

注意:本题相对原题稍作改动,只需返回未识别的字符数

示例:

输入:
dictionary = ["looked","just","like","her","brother"]
sentence = "jesslookedjustliketimherbrother"
输出: 7
解释: 断句后为"jess looked just like tim her brother",共7个未识别字符。


提示:

  • 0 <= len(sentence) <= 1000
  • dictionary中总字符数不超过 150000。
  • 你可以认为dictionary和sentence中只包含小写字母。

 

这一题直接采用dp和字典树来实现会比较简单

由后向前遍历字符串:dp[i]是以i开始的后半字符串中,无法匹配到词表的最小字符数
dp[i] = min(dp[i-j] (匹到单词长j), dp[i-j2] (匹到单词长j2),... , dp[i+1]+1(不匹配任何单词,依赖上一次匹配) )

func respace(dictionary []string, sentence string) int {
	if len(dictionary) == 0 || len(sentence) == 0 {
		return 0
	}
	// 构造字典树O(len)
	trie := NewTrie()
	for i := range dictionary {
		trie.Add(dictionary[i])
	}
	dp := make([]int, len(sentence))
	// 从后向前遍历,最差时间复杂度O(n*n)dp[i] = min(dp[i-j](匹到单词长j), dp[i-j2](匹到单词长j2),... )
	// "abc" i=2, j=3, s=c 查的到,则dp[2]=dp[3]
	for i := len(sentence)-1; i >= 0; i-- {
		// 这里置个初值,也就是默认当前字符匹不上任何单词
		if i < len(sentence)-1 {
			dp[i] = dp[i+1]+1
		} else {
			dp[i] = 1
		}
		// 这里查看i...len的每个可组成单词,并判断最小值
		for j := i+1; j <= len(sentence); j++ {
			if trie.Search(sentence[i:j]) {
				if j < len(sentence) {
					// 这里针对abcdef, 假如ab是个词, bcde是个词,那 dp[0] = min(dp[1]+1=2,dp[2]=4),还是不匹ab比较小
					dp[i] = min(dp[i], dp[j])
				} else {
					// 直接干到末尾,则置为0,以覆盖前面置的初值 dp[i] = dp[i+1]+1
					dp[i] = 0
				}
			}
		}
	}
	return dp[0]
}

func min(i,j int) int {
	if i < j {
		return i
	}
	return j
}

type Trie struct {
	child []*Trie
	value byte
	isEnd bool
}

func NewTrie() *Trie {
	return &Trie{
		child: make([]*Trie, 26),
	}
}

func (t *Trie) Search(s string) bool {
	node := t
	for i := range s {
		c := s[i] - 'a'
		if node.child[c] == nil {
			return false
		}
		node = node.child[c]
	}
	return node.isEnd
}

func (t *Trie) Add(s string) {
	node := t
	for i := range s {
		c := s[i] - 'a'
		if node.child[c] == nil {
			node.child[c] = &Trie{
				child: make([]*Trie, 26),
				value: s[i],
				isEnd: false,
			}
		}
		node = node.child[c]
	}
	node.isEnd = true
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值