前綴樹
是N叉樹
的一種特殊形式。一般來講,一個前綴樹是用來存儲字符串
的。前綴樹的每個節點表明一個字符串
(前綴
)。每個節點會有多個子節點,通往不一樣子節點的路徑上有着不一樣的字符。子節點表明的字符串是由節點自己的原始字符串
,以及通往該子節點路徑上全部的字符
組成的。html
下面是前綴樹的一個例子:node
在上圖示例中,咱們在節點中標記的值是該節點對應表示的字符串。例如,咱們從根節點開始,選擇第二條路徑 'b',而後選擇它的第一個子節點 'a',接下來繼續選擇子節點 'd',咱們最終會到達葉節點 "bad"。節點的值是由從根節點開始,與其通過的路徑中的字符按順序造成的。ios
值得注意的是,根節點表示空字符串
。c++
前綴樹的一個重要的特性是,節點全部的後代都與該節點相關的字符串有着共同的前綴。這就是前綴樹
名稱的由來。正則表達式
咱們再來看這個例子。例如,以節點 "b" 爲根的子樹中的節點表示的字符串,都具備共同的前綴 "b"。反之亦然,具備公共前綴 "b" 的字符串,所有位於以 "b" 爲根的子樹中,而且具備不一樣前綴的字符串來自不一樣的分支。算法
前綴樹有着普遍的應用,例如自動補全,拼寫檢查等等。咱們將在後面的章節中介紹實際應用場景。數組
在前面的文章中,咱們介紹了前綴樹的概念。在這篇文章中,咱們將討論如何用代碼表示這個數據結構。數據結構
在閱讀一下內容前,請簡要回顧N叉樹的節點結構。app
前綴樹的特別之處在於字符和子節點之間的對應關係。有許多不一樣的表示前綴樹節點的方法,這裏咱們只介紹其中的兩種方法。函數
方法一 - 數組
第一種方法是用數組
存儲子節點。
例如,若是咱們只存儲含有字母 a
到 z
的字符串,咱們能夠在每一個節點中聲明一個大小爲26的數組來存儲其子節點。對於特定字符 c
,咱們可使用 c - 'a'
做爲索引來查找數組中相應的子節點。
// change this value to adapt to different cases #define N 26 struct TrieNode { TrieNode* children[N]; // you might need some extra values according to different cases }; /** Usage: * Initialization: TrieNode root = new TrieNode(); * Return a specific child node with char c: (root->children)[c - 'a'] */
訪問子節點十分快捷
。訪問一個特定的子節點比較容易
,由於在大多數狀況下,咱們很容易將一個字符轉換爲索引。但並不是全部的子節點都須要這樣的操做,因此這可能會致使空間的浪費
。
方法二 - Map
第二種方法是使用 Hashmap
來存儲子節點。
咱們能夠在每一個節點中聲明一個Hashmap。Hashmap的鍵是字符,值是相對應的子節點。
struct TrieNode { unordered_map<char, TrieNode*> children; // you might need some extra values according to different cases }; /** Usage: * Initialization: TrieNode root = new TrieNode(); * Return a specific child node with char c: (root->children)[c] */
經過相應的字符來訪問特定的子節點更爲容易
。但它可能比使用數組稍慢一些
。可是,因爲咱們只存儲咱們須要的子節點,所以節省了空間
。這個方法也更加靈活
,由於咱們不受到固定長度和固定範圍的限制。
補充
咱們已經提到過如何表示前綴樹中的子節點。除此以外,咱們也須要用到一些其餘的值。
例如,咱們知道,前綴樹的每一個節點表示一個字符串,但並非全部由前綴樹表示的字符串都是有意義的。若是咱們只想在前綴樹中存儲單詞,那麼咱們可能須要在每一個節點中聲明一個布爾值(Boolean)做爲標誌,來代表該節點所表示的字符串是否爲一個單詞。
咱們已經在另外一張卡片中討論了 (如何在二叉搜索樹中實現插入操做)。
提問:
你還記得如何在二叉搜索樹中插入一個新的節點嗎?
當咱們在二叉搜索樹中插入目標值時,在每一個節點中,咱們都須要根據 節點值
和 目標值
之間的關係,來肯定目標值須要去往哪一個子節點。一樣地,當咱們向前綴樹中插入一個目標值時,咱們也須要根據插入的 目標值
來決定咱們的路徑。
更具體地說,若是咱們在前綴樹中插入一個字符串 S
,咱們要從根節點開始。 咱們將根據 S[0]
(S中的第一個字符),選擇一個子節點或添加一個新的子節點。而後到達第二個節點,並根據 S[1]
作出選擇。 再到第三個節點,以此類推。 最後,咱們依次遍歷 S 中的全部字符併到達末尾。 末端節點將是表示字符串 S 的節點。
下面是一個例子:
咱們來用僞代碼總結一下以上策略:
1. Initialize: cur = root 2. for each char c in target string S: 3. if cur does not have a child c: 4. cur.children[c] = new Trie node 5. cur = cur.children[c] 6. cur is the node which represents the string S
一般狀況狀況下,你須要本身構建前綴樹。構建前綴樹實際上就是屢次調用插入函數。但請記住在插入字符串以前要 初始化根節點
。
搜索前綴
正如咱們在前綴樹的簡介中提到的,全部節點的後代都與該節點相對應字符串的有着共同前綴。所以,很容易搜索以特定前綴開頭的任何單詞。
一樣地,咱們能夠根據給定的前綴沿着樹形結構搜索下去。一旦咱們找不到咱們想要的子節點,搜索就以失敗終止。不然,搜索成功。爲了更具體地解釋搜索的過程,咱們提供了下列示例:
咱們來用僞代碼總結一下以上策略:
1. Initialize: cur = root 2. for each char c in target string S: 3. if cur does not have a child c: 4. search fails 5. cur = cur.children[c] 6. search successes
搜索單詞
你可能還想知道如何搜索特定的單詞,而不是前綴。咱們能夠將這個詞做爲前綴,並一樣按照上述一樣的方法進行搜索。
提示:往每一個節點中加入布爾值可能會有效地幫助你解決這個問題。
實現一個 Trie (前綴樹),包含 insert
, search
, 和 startsWith
這三個操做。
示例:
Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // 返回 true trie.search("app"); // 返回 false trie.startsWith("app"); // 返回 true trie.insert("app"); trie.search("app"); // 返回 true
說明:
a-z
構成的。遞歸解法
#include <iostream> #include <vector> #include <map> using namespace std; /// Trie Recursive version class Trie{ private: struct Node{ map<char, int> next; bool end = false; }; vector<Node> trie; public: Trie(){ trie.clear(); trie.push_back(Node()); } /** Inserts a word into the trie. */ void insert(const string& word){ insert(0, word, 0); } /** Returns if the word is in the trie. */ bool search(const string& word){ return search(0, word, 0); } /** Returns if there is any word in the trie that starts with the given prefix. */ bool startsWith(const string& prefix) { return startsWith(0, prefix, 0); } private: void insert(int treeID, const string& word, int index){ if(index == word.size()) { trie[treeID].end = true; return; } if(trie[treeID].next.find(word[index]) == trie[treeID].next.end()){ trie[treeID].next[word[index]] = trie.size(); trie.push_back(Node()); } insert(trie[treeID].next[word[index]], word, index + 1); } bool search(int treeID, const string& word, int index){ if(index == word.size()) return trie[treeID].end; if(trie[treeID].next.find(word[index]) == trie[treeID].next.end()) return false; return search(trie[treeID].next[word[index]], word, index + 1); } bool startsWith(int treeID, const string& prefix, int index){ if(index == prefix.size()) return true; if(trie[treeID].next.find(prefix[index]) == trie[treeID].next.end()) return false; return startsWith(trie[treeID].next[prefix[index]], prefix, index + 1); } }; void printBool(bool res){ cout << (res ? "True" : "False") << endl; } int main() { Trie trie1; trie1.insert("ab"); printBool(trie1.search("a")); // false printBool(trie1.startsWith("a")); // true; cout << endl; // --- Trie trie2; trie2.insert("a"); printBool(trie2.search("a")); // true printBool(trie2.startsWith("a")); // true; return 0; }
非遞歸解法
#include <iostream> #include <vector> #include <map> using namespace std; /// Trie Recursive version class Trie{ private: struct Node{ map<char, int> next; bool end = false; }; vector<Node> trie; public: Trie(){ trie.clear(); trie.push_back(Node()); } void insert(const string& word){ int treeID = 0; for(char c: word){ //若未找到該節點 if(trie[treeID].next.find(c) == trie[treeID].next.end()){ trie[treeID].next[c] = trie.size(); trie.push_back(Node()); } treeID = trie[treeID].next[c]; } trie[treeID].end = true; } bool search(const string& word){ int treeID = 0; for(char c: word){ if(trie[treeID].next.find(c)==trie[treeID].next.end()) return false; treeID = trie[treeID].next[c]; } return trie[treeID].end; } bool startsWith(const string& prefix){ int treeID = 0; for(char c: prefix){ if(trie[treeID].next.find(c)==trie[treeID].next.end()) return false; treeID = trie[treeID].next[c]; } return true; } }; void printBool(bool res){ cout << (res? "True" : "False") << endl; } int main() { Trie trie1; trie1.insert("ab"); printBool(trie1.search("a")); // false printBool(trie1.startsWith("a")); // true; cout << endl; // --- Trie trie2; trie2.insert("a"); printBool(trie2.search("a")); // true printBool(trie2.startsWith("a")); // true; return 0; }
實現一個 MapSum 類裏的兩個方法,insert
和 sum
。
對於方法 insert
,你將獲得一對(字符串,整數)的鍵值對。字符串表示鍵,整數表示值。若是鍵已經存在,那麼原來的鍵值對將被替代成新的鍵值對。
對於方法 sum
,你將獲得一個表示前綴的字符串,你須要返回全部以該前綴開頭的鍵的值的總和。
示例 1:
輸入: insert("apple", 3), 輸出: Null 輸入: sum("ap"), 輸出: 3 輸入: insert("app", 2), 輸出: Null 輸入: sum("ap"), 輸出: 5
參考http://www.javashuo.com/article/p-agweyvri-ep.html
#include <iostream> #include <bits/stdc++.h> using namespace std; //這道題讓咱們實現一個MapSum類,裏面有兩個方法,insert和sum,其中inser就是插入一個鍵值對,而sum方法比較特別,是在找一個前綴,須要將全部有此前綴的單詞的值累加起來返回。看到這種玩前綴的題,照理來講是要用前綴樹來作的。可是博主通常想偷懶,不想新寫一個結構或類,因而就使用map來代替前綴樹啦。博主開始想到的方法是創建前綴和一個pair之間的映射,這裏的pair的第一個值表示該詞的值,第二個值表示將該詞做爲前綴的全部詞的累加值,那麼咱們的sum函數就異常的簡單了,直接將pair中的兩個值相加便可。關鍵就是要在insert中把數據結構建好,構建的方法也不難,首先咱們suppose本來這個key是有值的,咱們更新的時候只須要加上它的差值便可,就算key不存在默認就是0,算差值也沒問題。而後咱們將first值更新爲val,而後就是遍歷其全部的前綴了,給每一個前綴的second都加上diff便可,參見代碼以下: class MapSum{ private: unordered_map<string, pair<int, int>> m; public: MapSum(){} void insert(string key, int val){ //diff的做用防止重複插入 int diff = val - m[key].first, n = key.size(); m[key].first = val; for(int i=n-1; i>0; --i) m[key.substr(0, i)].second += diff; } int sum(string prefix){ return m[prefix].first + m[prefix].second; } }; //下面這種方法是論壇上投票最高的方法,感受很叼,用的是帶排序的map,insert就是把單詞加入map。在map裏會按照字母順序自動排序,而後在sum函數裏,咱們根據prefix來用二分查找快速定位到第一個不小於prefix的位置,而後向後遍歷,向後遍歷的都是以prefix爲前綴的單詞,若是咱們發現某個單詞不是以prefix爲前綴了,直接break;不然就累加其val值,參見代碼以下: class MapSum{ private: map<string, int> m; public: MapSum(){} void insert(string key, int val){ m[key] = val; } int sum(string prefix){ int res = 0, n = prefix.size(); for(auto it = m.lower_bound(prefix); it != m.end(); ++it){ if(it->first.substr(0, n) != prefix) break; res += it->second; } return res; } };
在英語中,咱們有一個叫作 詞根
(root)的概念,它能夠跟着其餘一些詞組成另外一個較長的單詞——咱們稱這個詞爲 繼承詞
(successor)。例如,詞根an
,跟隨着單詞 other
(其餘),能夠造成新的單詞 another
(另外一個)。
如今,給定一個由許多詞根組成的詞典和一個句子。你須要將句子中的全部繼承詞
用詞根
替換掉。若是繼承詞
有許多能夠造成它的詞根
,則用最短的詞根替換它。
你須要輸出替換以後的句子。
示例 1:
輸入: dict(詞典) = ["cat", "bat", "rat"] sentence(句子) = "the cattle was rattled by the battery" 輸出: "the cat was rat by the bat"
注:
參考https://www.cnblogs.com/grandyang/p/7423420.html
#include <iostream> #include <vector> #include <sstream> using namespace std; //這道題最好的解法實際上是用前綴樹(Trie / Prefix Tree)來作,關於前綴樹使用以前有一道很好的入門題Implement Trie (Prefix Tree)。瞭解了前綴樹的原理機制,那麼咱們就能夠發現這道題其實很適合前綴樹的特色。咱們要作的就是把全部的前綴都放到前綴樹裏面,並且在前綴的最後一個結點的地方將標示isWord設爲true,表示從根節點到當前結點是一個前綴,而後咱們在遍歷單詞中的每個字母,咱們都在前綴樹查找,若是當前字母對應的結點的表示isWord是true,咱們就返回這個前綴,若是當前字母對應的結點在前綴樹中不存在,咱們就返回原單詞,這樣就能完美的解決問題了。因此啊,之後遇到了有關前綴或者相似的問題,必定不要忘了前綴樹這個神器喲~ class Solution{ public: class TrieNode{ public: bool isWord; TrieNode *child[26]; // TrieNode(){}; TrieNode(){ isWord = false; for(auto &a : child) a = NULL; }; }; string replaceWords(vector<string>& dict, string sentence){ string res = "", t=""; istringstream is(sentence); TrieNode* root = new TrieNode(); for(string word: dict){ insert(root, word); } while(is >> t){ if(!res.empty()) res += " "; res += findPrefix(root, t); } return res; } void insert(TrieNode* node, string word){ for(char c: word){ if(!node->child[c-'a']) node->child[c-'a'] = new TrieNode(); node = node->child[c-'a']; } node->isWord = true; } string findPrefix(TrieNode* node, string word){ string cur = ""; for(char c: word){ if(!node->child[c-'a']) break; cur.push_back(c); node = node->child[c - 'a']; if(node->isWord) return cur; } return word; } };
設計一個支持如下兩種操做的數據結構:
void addWord(word) bool search(word)
search(word) 能夠搜索文字或正則表達式字符串,字符串只包含字母 .
或 a-z
。 .
能夠表示任何一個字母。
示例:
addWord("bad") addWord("dad") addWord("mad") search("pad") -> false search("bad") -> true search(".ad") -> true search("b..") -> true
說明:
你能夠假設全部單詞都是由小寫字母 a-z
組成的。
#include <iostream> #include <vector> using namespace std; class WordDictionary{ private: struct TrieNode{ bool isWord; vector<TrieNode*> children; TrieNode(): isWord(false), children(26, nullptr){} ~TrieNode(){ for(TrieNode* child: children) if(child) delete child; } }; TrieNode* trieRoot; bool myFind(string &str, TrieNode* nowPtr, int nowIndex){ int strSize = str.size(); if(nowPtr == NULL){ return false; } if(nowIndex >= strSize){ if(nowPtr->isWord){ return true; } return false; } else if(str[nowIndex] != '.'){ if(nowPtr->children[str[nowIndex] - 'a'] != NULL){ return myFind(str, nowPtr->children[str[nowIndex] - 'a'], nowIndex+1); } return false; } else{ for(int i=0; i<26; ++i){ if(nowPtr->children[i] != NULL && myFind(str, nowPtr->children[i], nowIndex+1 )){ return true; } } } return false; } public: WordDictionary(){ trieRoot = new TrieNode(); } void addWord(string word){ TrieNode * ptr = trieRoot; for(auto ch : word){ if(ptr->children[ch - 'a'] == NULL){ ptr->children[ch - 'a'] = new TrieNode(); } ptr = ptr->children[ch - 'a']; } ptr->isWord = true; } bool search(string word){ return myFind(word, trieRoot, 0); } };
給定一個非空數組,數組中元素爲 a0, a1, a2, … , an-1,其中 0 ≤ ai < 2^31 。
找到 ai 和aj 最大的異或 (XOR) 運算結果,其中0 ≤ i, j < n 。
你能在O(n)的時間解決這個問題嗎?
示例:
輸入: [3, 10, 5, 25, 2, 8] 輸出: 28 解釋: 最大的結果是 5 ^ 25 = 28.
//https://blog.csdn.net/weijiechenlun133/article/details/70135937 class SolutionA { public: int findMaximumXOR(vector<int> &nums) { if (nums.size() < 2) return 0; int maxNum = 0; int flag = 0; for(int i = 31; i>=0; --i){ set<int> hash; flag |= (1 << i); for(int x:nums) hash.insert(flag & x); int tmp = maxNum | (1<<i); for(int x:hash){ if(hash.find(x^tmp)!=hash.end()){ maxNum = tmp; break; } } } return maxNum; } }; struct Node{ Node* next[2]; Node(){ next[0] = nullptr; next[1] = nullptr; } }; class SolutionB{ public: void buildTrieTree(Node* root, int x){ for(int i = 31; i>=0; --i){ int flag = (x & (1<<i) )? 1:0; if(root->next[flag] == nullptr){ root->next[flag] = new Node(); } root = root->next[flag]; } } int findMaxXorInTire(Node* root, int x){ int result = 0; for(int i = 31; i>=0; --i){ int flag = (x & (1<<i) )? 0:1; if(root->next[flag] != nullptr){ result |= (1<<i); //result = result | (1<<i) root = root->next[flag]; } else root = root->next[1-flag]; } return result; } int findMaximumXOR(vector<int>& nums){ if(nums.size()<2) return 0; Node head; for(int x : nums) buildTrieTree(&head, x); int maxNum = 0; for(int x: nums){ int m = findMaxXorInTire(&head, x); maxNum = max(maxNum, m); } return maxNum; } };
給定一個二維網格 board 和一個字典中的單詞列表 words,找出全部同時在二維網格和字典中出現的單詞。
單詞必須按照字母順序,經過相鄰的單元格內的字母構成,其中「相鄰」單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母在一個單詞中不容許被重複使用。
示例:
輸入: words = ["oath","pea","eat","rain"] and board = [ ['o','a','a','n'], ['e','t','a','e'], ['i','h','k','r'], ['i','f','l','v'] ] 輸出: ["eat","oath"]
說明:
你能夠假設全部輸入都由小寫字母 a-z
組成。
提示:
參考:https://blog.csdn.net/qq_41855420/article/details/88064909
#include <iostream> #include <vector> using namespace std; //前綴樹的程序表示 class TrieNode { public: bool isWord;//當前節點爲結尾是不是字符串 vector<TrieNode*> children; TrieNode() : isWord(false), children(26, nullptr) {} ~TrieNode() { for (TrieNode* child : children) if (child) delete child; } }; class Solution { private: TrieNode * trieRoot;//構建的單詞前綴樹 //在樹中插入一個單詞的方法實現 void addWord(string word) { TrieNode *ptr = trieRoot;//掃描這棵樹,將word插入 //將word的字符逐個插入 for (auto ch : word) { if (ptr->children[ch - 'a'] == NULL) { ptr->children[ch - 'a'] = new TrieNode(); } ptr = ptr->children[ch - 'a']; } ptr->isWord = true;//標記爲單詞 } public: int rowSize;//board的行數 int colSize;//board的列數 vector<vector<bool>> boardFlag;//標記board[row][col]是否已使用 //以board[row][col]爲中心點,四個方向進行嘗試搜索 void dfs(vector<vector<char>>& board, vector<string> &result, string &tempRes, TrieNode * nowRoot, int row, int col) { if (nowRoot == NULL) { return; } if (nowRoot->isWord) {//若是這個單詞成功找到 result.push_back(tempRes);//放入結果 nowRoot->isWord = false;//將這個單詞標記爲公共後綴 防止重複 } string tempResAdd; //上方測試 //若是上方未出界,沒有被使用,且nowRoot->children中存在相等的節點 if (row - 1 >= 0 && !boardFlag[row - 1][col] && nowRoot->children[board[row - 1][col] - 'a'] != NULL) { boardFlag[row - 1][col] = true;//標記使用 tempResAdd = tempRes + char(board[row - 1][col]); dfs(board, result, tempResAdd, nowRoot->children[board[row - 1][col] - 'a'], row - 1, col); boardFlag[row - 1][col] = false;//取消標記 } //下方測試 //若是下方未出界,沒有被使用,且nowRoot->children中存在相等的節點 if (row + 1 < rowSize && !boardFlag[row + 1][col] && nowRoot->children[board[row + 1][col] - 'a'] != NULL) { boardFlag[row + 1][col] = true;//標記使用 tempResAdd = tempRes + char(board[row + 1][col]); dfs(board, result, tempResAdd, nowRoot->children[board[row + 1][col] - 'a'], row + 1, col); boardFlag[row + 1][col] = false;//取消標記 } //左方測試 //若是左方未出界,沒有被使用,且nowRoot->children中存在相等的節點 if (col - 1 >= 0 && !boardFlag[row][col - 1] && nowRoot->children[board[row][col - 1] - 'a'] != NULL) { boardFlag[row][col - 1] = true;//標記使用 tempResAdd = tempRes + char(board[row][col - 1]); dfs(board, result, tempResAdd, nowRoot->children[board[row][col - 1] - 'a'], row, col - 1); boardFlag[row][col - 1] = false;//取消標記 } //右方測試 //若是右方未出界,沒有被使用,且nowRoot->children中存在相等的節點 if (col + 1 < colSize && !boardFlag[row][col + 1] && nowRoot->children[board[row][col + 1] - 'a'] != NULL) { boardFlag[row][col + 1] = true;//標記使用 tempResAdd = tempRes + char(board[row][col + 1]); dfs(board, result, tempResAdd, nowRoot->children[board[row][col + 1] - 'a'], row, col + 1); boardFlag[row][col + 1] = false;//取消標記 } } vector<string> findWords(vector<vector<char>>& board, vector<string>& words) { rowSize = board.size(); if (rowSize == 0) { return {}; } colSize = board[0].size(); boardFlag = vector<vector<bool>>(rowSize, vector<bool>(colSize, false));//構建標記容器 trieRoot = new TrieNode();//單詞後綴樹 //將單詞都放入前綴樹中 for (auto word : words) { addWord(word); } vector<string> result;//用於存儲結果 string tempRes; for (int row = 0; row < rowSize; ++row) { for (int col = 0; col < colSize; ++col) { if (trieRoot->children[board[row][col] - 'a'] != NULL) {//搜索 tempRes = ""; tempRes += char(board[row][col]); boardFlag[row][col] = true;//標記使用 dfs(board, result, tempRes, trieRoot->children[board[row][col] - 'a'], row, col); boardFlag[row][col] = false;//取消使用 } } } return result; } };
給定一組惟一的單詞, 找出全部不一樣 的索引對(i, j)
,使得列表中的兩個單詞, words[i] + words[j]
,可拼接成迴文串。
示例 1:
輸入: ["abcd","dcba","lls","s","sssll"] 輸出: [[0,1],[1,0],[3,2],[2,4]] 解釋: 可拼接成的迴文串爲 ["dcbaabcd","abcddcba","slls","llssssll"]
示例 2:
輸入: ["bat","tab","cat"] 輸出: [[0,1],[1,0]] 解釋: 可拼接成的迴文串爲 ["battab","tabbat"]
大多數解法都是基於hash表,看着很複雜,我找到一個可讀性比較高的版本,以後還得拿出來溫習。
#include <iostream> #include <vector> #include <bits/stdc++.h> #include <string> using namespace std; class Solution{ public: bool isPalindrome(string& s, int start, int end){ while(start < end) if(s[start++] != s[end--]) return false; return true; } vector<vector<int>> palindromePairs(vector<string> words){ vector<vector<int>> ans; unordered_map<string, int> dict; int len = words.size(); for(int i=0; i<len; i++) dict[words[i]] = i; for(int i=0; i<len; i++){ string cur = words[i]; int clen = cur.size(); for(int j=0; j<=clen; j++){ //找後綴 if(isPalindrome(cur, j, clen - 1)){ string suffix = cur.substr(0, j); reverse(suffix.begin(), suffix.end()); if(dict.find(suffix)!=dict.end() && i!=dict[suffix]) ans.push_back({i, dict[suffix]}); } //找前綴 if(j>0 && isPalindrome(cur, 0, j-1)){ string prefix = cur.substr(j); reverse(prefix.begin(), prefix.end()); if(dict.find(prefix) != dict.end() && i!=dict[prefix]) ans.push_back({dict[prefix], i}); } } } return ans; } }; int main(){ vector<string> a = {"lls", "s", "sssll"}; Solution s = Solution(); vector<vector<int>> v = s.palindromePairs(a); };