【LeetCode】720. Longest Word in Dictionary(C++)

地址:https://leetcode.com/problems/longest-word-in-dictionary/

题目:

Given a list of strings words representing an English Dictionary, find the longest word in words that can be built one character at a time by other words in words. If there is more than one possible answer, return the longest word with the smallest lexicographical order.

Example 1:

Input: words = [“w”,“wo”,“wor”,“worl”, “world”]
Output: “world”
Explanation: The word “world” can be built one character at a time by “w”, “wo”, “wor”, and “worl”.

Example 2:

Input: words = [“a”, “banana”, “app”, “appl”, “ap”, “apply”, “apple”]
Output: “apple”
Explanation: Both “apply” and “apple” can be built from other words in the dictionary. However, “apple” is lexicographically smaller than “apply”.

Note:

All the strings in the input will only contain lowercase letters.

  • The length of words will be in the range [1, 1000].
  • The length of words[i] will be in the range [1, 30].

理解:

承认愚蠢的我并没有看懂题目。。遂google之
是这样的,找到words中最长的那个单词,他可以通过其他的words每次加一个字母得到。比如Example 1中,world可以依次由"w",“wo”,“wor”,"worl"添加’o’,‘r’,‘l’,'d’得到。如果有多个可能的答案,返回按字典序最小的,也就是最后一个字母最小的,比如apple和apply,字典序apple在前,返回apple。

实现1:

看到discussion里的Trie+DFS,感觉略麻烦,要去吃饭了,先看一种简单的实现。
首先把words按字典序排序,从而保证字典序小的word在前,然后遍历。创建一个unordered_set

  • 如果长度为1,就加入s,因为可能是其他单词的前缀,并更新res
  • 如果这个单词word的前word.size()-1项在set中,表明word可以由words中的单词添加一个字母构成,就更新res,并加入s
  • 注意更新res的时候,只有长度大于现有的res才更新,这样保证了,res为同长度下字典序最小的字符串。

实现如下

class Solution {
public:
	string longestWord(vector<string>& words) {
		string res;
		unordered_set<string> s;
		sort(words.begin(), words.end());
		for (string word : words) {
			if (word.size() == 1 || s.count(word.substr(0, word.size() - 1))) {
				res = word.size() > res.size() ? word : res;
				s.insert(word);
			}
		}
        return res;
	}
};

先去吃饭了,下午再补复杂的方法。

实现2

先补一些基础知识
trie:又称前缀树或字典树,是一种有序树,用于保存关联数组,其中的键通常是字符串。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。一个节点的所有子孙都有相同的前缀,也就是这个节点对应的字符串,而根节点对应空字符串。一般情况下,不是所有的节点都有对应的值,只有叶子节点和部分内部节点所对应的键才有相关的值。Trie-维基百科
基本性质:

  • 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
  • 从根节点到某一个节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符互不相同。
    一棵Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。
    上面的Trie树,表示了关键字集合{“a”, “to”, “tea”, “ted”, “ten”, “i”, “in”, “inn”} 。来源:Trie树(Prefix Tree)介绍
    因此,插入所有单词,构成一个Tier即可。实现如下:
//Definition of TrieNode
class TrieNode {
public:
	bool last;
	vector<TrieNode*> children;
	TrieNode():last(false),children(26,nullptr){}
};

class Trie {
public:
	TrieNode* root;
	Trie():root(new TrieNode()){}
	void insert(string s) {
		TrieNode* curr = root;
		for (char c : s) {
			int index = c - 'a';
			if (!curr->children[index]) {
				curr->children[index] = new TrieNode();
			}
			curr = curr->children[index];
		}
		curr->last = true;
	}
	void dfs(TrieNode* curr, string& res, string& s) {
		if (curr->last) {
			if (s.size() > res.size())
				res = s;
		}
		for (int index = 0; index < 26;++index) {
			if (curr->children[index]&& curr->children[index]->last) {
				s.push_back(index + 'a');
				dfs(curr->children[index], res, s);
				s.pop_back();
			}
		}
	}

	string dfsSearch() {
		string res, s;
		dfs(root, res, s);
		return res;
	}
};

class Solution {
public:
	string longestWord(vector<string>& words) {
		Trie t;
		for (string word : words)
			t.insert(word);
		return t.dfsSearch();
	}
};

实现3

提交完了以后,还看到一种简单粗暴的方法。
对words建立一个unordered_set,然后遍历words,对每个word,

  • 若比res短,或者相同,但字典序大于等于res时,跳过
  • 否则,循环去判断,每去掉一个后缀,剩余的是不是在哈希表里,如果有不在的就不满足,如果都在,则标记为新的res
class Solution {
public:
	string longestWord(vector<string>& words) {
		unordered_set<string> s(words.size());
		for (auto &w : words) s.insert(w);

		string rs;
		for (auto &w : words) {
			if (w.size() < rs.size() || (w.size() == rs.size() && w >= rs)) continue;

			bool found = true;
			string temp = w;
			while (temp.size() > 1) {
				temp.pop_back();
				if (!s.count(temp)) { found = false; break; }
			}

			if (found) rs = w;
		}
		return rs;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值