其实最长公共前缀这个题目很容易就能想到水平扫描(两两比较,逐渐缩小范围)或者垂直扫描(同时比较所有字符串的同一位),而且性能很好;稍微高级一点的想法大概是分治,但是分治占用的空间有点大,毕竟是基于递归的。据说这种题目的通用解法是前缀树,但是前缀树我没用过,所以记录一下。
C++实现和Java实现还是有点区别的,尤其是在对null
的判断上。在Java里可以很轻易地进行类似于object == null
这样的判断,但是在C++里就必须用指针。
// 结点
class TrieNode
{
private:
TrieNode *next[26] = {nullptr};
bool isEnd;
// 非空子节点的数量
int size = 0;
public:
bool containsKey(char ch)
{
return next[ch - 'a'] != nullptr;
}
TrieNode *get(char ch)
{
return next[ch - 'a'];
}
void put(char ch, TrieNode *node)
{
next[ch - 'a'] = node;
++size;
}
void setEnd()
{
isEnd = true;
}
bool getEnd()
{
return isEnd;
}
int getLinks()
{
return size;
}
};
// 树
class Trie
{
private:
TrieNode *root;
// search a prefix or whole key in trie and
// returns the node where search ends
TrieNode *searchPrefix(string &word)
{
TrieNode *node = root;
for (auto c : word)
{
if (node->containsKey(c))
{
node = node->get(c);
}
else
{
return nullptr;
}
}
return node;
}
public:
/** Initialize your data structure here. */
Trie()
{
root = new TrieNode();
}
/** Inserts a word into the trie. */
void insert(string word)
{
TrieNode *node = root;
for (auto c : word)
{
if (!node->containsKey(c))
{
node->put(c, new TrieNode());
}
node = node->get(c);
}
node->setEnd();
}
/** Returns if the word is in the trie. */
bool search(string word)
{
TrieNode *node = searchPrefix(word);
return node != nullptr && node->getEnd();
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix)
{
TrieNode *node = searchPrefix(prefix);
return node != nullptr;
}
string searchLongestPrefix(const string &word)
{
TrieNode *node = root;
string prefix;
for (auto c : word)
{
if (node->containsKey(c) &&
node->getLinks() == 1 &&
!node->getEnd())
{
prefix += c;
node = node->get(c);
}
else
{
return prefix;
}
}
return prefix;
}
};
至于有效括号,很显然用辅助栈就可以解决。但是在选择上有一些讲究,在这种简单的场景下,我们完全可以用数组自己实现一个栈,这样可以减少空间的占用。
感觉上,在简单的情况下,数组比栈和map快,switch-case比map快;可能这就是减少对外部实体的依赖。
看到一句话,觉得很有道理:栈可以辅助我们进行从外向内的递归。这对于不清楚结构的问题是很有效的。