【LeetCode 745】Prefix and Suffix Search

Given many words, words[i] has weight i.

Design a class WordFilter that supports one function, WordFilter.
f(String prefix, String suffix). It will return the word with given prefix and suffix with maximum weight. 
If no word exists, return -1.

题意:设计一个WordFilter类,它提供一个基本的搜索函数WordFilter.f(String prefix, String suffix),
根据前缀prefix和后缀suffix查询权值最大的单词并返回其下标。

Examples:
Input:
WordFilter(["apple"])
WordFilter.f("a", "e") // returns 0
WordFilter.f("b", "") // returns -1
Note:
words has length in range [1, 15000].
For each test case, up to words.length queries WordFilter.f may be made.
words[i] has length in range [1, 10].
prefix, suffix have lengths in range [0, 10].
words[i] and prefix, suffix queries consist of lowercase letters only.

思路:凡是涉及大量单词的频繁查询,必用到前缀树(字典树)我们定义两棵字典树,其中一棵存储正序单词,另一颗存储逆序单词。在构造函数中,我们分别将每个单词的正序和逆序加入到对应的字典树中即可。在f函数中,我们首先找到前缀为prefix的所有单词所对应的索引(其值等同于权重),然后找到后缀为suffix的所有单词所对应的索引。接着的问题就是在这两个索引列表中查找共同最大元素了。

构造前缀树类(字典树/TRIE树)

关于前缀树的内容,戳这里,本文不再赘述。trieNode结点上增加一个存储单词索引值的变量,查找时返回所有符合条件的单词的索引值。

struct trieNode {
	bool isEnd;
	int index;
	vector<trieNode*> children;
	trieNode() {
		isEnd = false;
		index = -1;
		children = vector<trieNode*>(26, 0);
	}
};
//向前缀树root中插入新单词word,其索引值为index;
//word参数不要使用引用,这样addWord函数可以直接以常字符串为输入;
void addWord(trieNode *root, string word, int index);
//根据前缀查询单词,返回所有符合条件的单词的索引值
vector<int> findWord(string& pre);
//两个字典树的头指针
trieNode *forward_root, *backward_root;

addWord 和 findWord 函数的具体实现如下。其中查询函数因为要找到所有复合条件的单词,所以先用前缀定位到符合条件的结点,那么从这个结点开始向下延伸的所有路径都是符合条件的解,显而易见定位之后需要用到 DFS.

void addWord(trieNode *root, string word, int index) { 
	for (auto i : word) {
		int idx = i - 'a';
		if (root->children[idx] == nullptr)
			root->children[idx] = new trieNode();
		root = root->children[idx];
	}
	root->index = index;
	root->isEnd = true;
}
vector<int> findWord(trieNode *root,string& pre) {
	vector<int> ans;
	for (auto i : pre) {
		int idx = i - 'a';
		if (root->children[idx] == nullptr)
			return ans;
		root = root->children[idx];
	}
	DFS(root, ans);
	return ans;
}
void DFS(trieNode *root, vector<int>& ans) {
	if (root == nullptr) return;
	if (root->isEnd == true) ans.push_back(root->index);
	for (auto i : root->children) 
		DFS(i, ans);
}

将单词依次添加进这两个字典树中

将单词word插入prefix树,将word逆转并插入suffix树。

void WordFilter(vector<string> words) {
	forward_root = new trieNode();
	backward_root = new trieNode();
	for (int i = 0; i < words.size();i++) {
		string w = words[i];
		addWord(forward_root, w, i);
		reverse(w.begin(), w.end());
		addWord(backward_root, w, i);
	}
}

根据前缀和后缀查找单词

先根据前缀在forward_root字典树中查找所有符合条件的单词索引,再根据后缀在backward_root字典树中查找,两个是完全镜像的过程,后缀反转之后变成前缀,所以步骤和前面一样。最后从这两组索引数组中找出权值最大的那个共同索引。

从两个数组中找出最大的共同元素,可以先把两个数组排序,然后从数组末尾找起,用两个索引指向末尾。比较两数组索引处的元素大小,若相同则该元素就是最大的共同元素,否则较大元素所在数组的末尾索引减一。

int f(string prefix, string suffix) {
	vector<int> preIdx, sufIdx;
	//根据前缀在第一个字典树中查找
	preIdx = findWord(forward_root, prefix);
	//将后缀反转,并在第二个字典树中查找
	reverse(suffix.begin(), suffix.end());
	sufIdx = findWord(backward_root, prefix);
	//将两个索引值数组排序
	sort(preIdx.begin(), preIdx.end());
	sort(sufIdx.begin(), sufIdx.end());
	//从数组末尾开始找起,若元素相同则就是最大共同元素
	auto preItr = preIdx.rbegin(), sufItr = sufIdx.rbegin();
	while (preItr != preIdx.rend() && sufItr != sufIdx.rend()) {
		if (*preItr == *sufItr)
			return *preItr;
		else if (*preItr > *sufItr)
			++preItr;
		else
			++sufItr;
	}
	return -1;
}

猜你喜欢

转载自blog.csdn.net/sinat_38972110/article/details/82935507