Trie( prefix tree ) 原理、C++实现及应用

Trie( prefix tree ) 原理、C++实现及应用

Trie

There are several data structures, like balanced trees and hash tables, which give us the possibility to search for a word in a dataset of strings. Then why do we need trie? Although hash table has O ( 1 ) O(1) O(1) time complexity for looking for a key, it is not efficient in the following operations :

  • Finding all keys with a common prefix.
  • Enumerating a dataset of strings in lexicographical order.

Another reason why trie outperforms hash table, is that as hash table increases in size, there are lots of hash collisions and the search time complexity could deteriorate to O ( n ) O(n) O(n), where nn is the number of keys inserted. Trie could use less space compared to Hash Table when storing many keys with the same prefix. In this case using trie has only O ( m ) O(m) O(m) time complexity, where mm is the key length. Searching for a key in a balanced tree costs O ( m log ⁡ n ) O(m \log n) O(mlogn) time complexity.

基本结构

题目来源于LeetCode, 代码上传后测试通过
使用unique_ptr,构建数据结构,在一定程度上减少memory leak

#include "stdafx.h"
#include<iostream>
#include<memory>
#include<algorithm>
#define ALPHABET_SIZE 26

class Trie {

public:
	bool isEnd;
	unique_ptr<Trie> links[ALPHABET_SIZE];
	/** Initialize your data structure here. */
	Trie() {
		this->isEnd = false;
	}

	bool containsChar(char ch) {
		return links[ch - 'a'] != nullptr;
	}
	void put(char ch, Trie* newt) {
		links[ch - 'a'].reset(newt);
	}

	Trie* get(char ch) {
		return links[ch - 'a'].get();
	}

	/** Inserts a word into the trie. */
	void insert(string word) {
		// start from root node
		Trie* root = this;
		for (int i = 0; i < word.length(); i++) {
			// create a new node if path doesn't exists
			if (!root->containsChar(word[i])) {
				root->put(word[i], new Trie());
			}
			root = root->get(word[i]);
		}
		// make current node as a keyword
		root->isEnd = true;
	}

	/** Returns if the word is in the trie. */
	bool search(string word) {
		if (this == nullptr)
			return false;
		Trie* root = this;
		for (int i = 0; i < word.length(); i++) {
			root = root->get(word[i]);
			if (root == nullptr)
				return false;
		}
		return root->isEnd;
	}

	/** Returns if there is any word in the trie that starts with the given prefix. */
	bool startsWith(string prefix) {
		if (this == nullptr)
			return false;
		Trie* root = this;
		int i = 0;
		for (; i < prefix.length(); i++) {
			root = root->get(prefix[i]);
			if (root == nullptr)
				return false;
		}
		return i == prefix.length();
	}

	~Trie(){}
};

回溯查找

class WordDictionary {
public:
	unique_ptr<Trie> t;
	/** Initialize your data structure here. */
	WordDictionary() {
		t.reset(new Trie());
	}

	/** Adds a word into the data structure. */
	void addWord(string word) {
		t->insert(word);
	}

	/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
	bool search(string word) {
		return searchUtil(t.get(), word, 0);
	}

	bool searchUtil(Trie* t, const string& word, int index) {
		if (!t)
			return false;
		if (index == word.length())
			return t->isEnd;
		char c = word[index];
		if (c == '.') {
			for (int i = 0; i < ALPHABET_SIZE; i++) {
				if (searchUtil(t->get('a' + i), word, index + 1))
					return true;
			}
		}
		else {
			return searchUtil(t->get(c), word, index + 1);
		}
		return false;
	}

	~WordDictionary(){}
};

找词根

In English, we have a concept called root, which can be followed by some other words to form another longer word - let’s call this word successor. For example, the root an, followed by other, which can form another word another.

Now, given a dictionary consisting of many roots and a sentence. You need to replace all the successor in the sentence with the root forming it. If a successor has many roots can form it, replace it with the root with the shortest length.

You need to output the sentence after the replacement.

Example 1:

Input: dict = [“cat”,“bat”,“rat”], sentence = “the cattle was rattled by the battery”
Output: “the cat was rat by the bat”

class ReplaceWords {
public:
	unique_ptr<Trie> t;

	ReplaceWords() {
		t.reset(new Trie());
	}
	string replaceWords(vector<string>& dict, string sentence) {
		for (auto elem : dict) {
			t->insert(elem);
		}
		string ans;
		istringstream iss(sentence);
		string curr;
		while (iss >> curr) {
			Trie* root = t.get();
			int i = 0;
			for (; i < curr.length(); i++) {
				root = root->get(curr[i]);
				if (!root){
					ans.append(curr + " ");
					break;
				}
				if (root->isEnd){
					ans.append(curr.substr(0, i + 1) + " ");
					break;
				}
			}
			if (i == curr.length()) {
				ans.append(curr + " ");
			}
			
		}
		ans.erase(ans.length() - 1);
		return ans;
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值