其實最長公共前綴這個題目很容易就能想到水平掃描(兩兩比較,逐漸縮小範圍)或者垂直掃描(同時比較所有字符串的同一位),而且性能很好;稍微高級一點的想法大概是分治,但是分治佔用的空間有點大,畢竟是基於遞歸的。據說這種題目的通用解法是前綴樹,但是前綴樹我沒用過,所以記錄一下。
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快;可能這就是減少對外部實體的依賴。
看到一句話,覺得很有道理:棧可以輔助我們進行從外向內的遞歸。這對於不清楚結構的問題是很有效的。