給咱們1e5個由小寫字母構成的不重複的字符串,每一個字符串長度不超過6,以後是1e5次查詢操做,每次給咱們一個字符串,要求咱們判斷這個字符串是否出現過,若是是則求出它是多少個其餘的字符串的前綴,並在以後的操做中無視這個字符串(刪除)。ios
可知:數據結構
- 每一條邊表明一個字符 - 節點不爲0表明從根節點到此爲一個完整的字符串
實現的方法也比較簡單,創建一棵單向樹,每一個節點都有spa
26個子節點(全部小寫字母);code
一個isstr,bool值,表明這裏是否爲一個字符串的結束blog
一個vis,int值,表明到這裏已經有多少個字符串遍歷過(是多少字符串的前綴)排序
一開始樹爲空,咱們每獲得一個字符串,就從它的第一個字符開始,從根節點遍歷,沒有對應的節點就建立,同時把所通過的節點的vis值加一,到最後字符串終止時,在終止的節點處置isstr爲true。字符串
從樹的形狀就能夠看出,這是一棵專門查詢字符串存在與否的數據結構(同時也付出了巨大的空間代價)。查詢操做很簡單,從根節點開始,按照要查詢的字符串的每一位來遍歷,若是遇到空節點或者終止時的節點的isstr爲false,則字符串不存在,不然存在。get
這個就是讀取要查詢的字符串的終止節點的vis值便可string
首先查詢成功以後,咱們從底部開始回溯刪除這個串的信息,將終止節點的isstr置爲false,同時將路過的vis值減一,若是vis值減爲0則將將此節點在其父節點中刪除便可。hash
給咱們一個序列A和序列B,要求咱們找到B序列的一種排列,使得
中的序列C字典序最小,並輸出序列C,長度<=3e5,每一個數小於2^30
首先按照運算規則C xor B == A意味着A xor B == C 也就是說 尋找一種B的排列,使得A逐個與B異或的結果的字典序最小
3e5基本不可能在其餘操做中間搞什麼排序了,應該從異或運算的結果出發,咱們追求結果的字典序最小,也就是說,對於每一個Ai,咱們都要找到一個Bj,使得其異或結果最小。這個若是直接找的話,是比較難的。可是若是使用Trie的話,能夠直接求得每個Ai的最小結果,方法以下:
對B序列創建一棵只包含01字符的Trie(也就是二叉樹),咱們規定A與B的每個數都是30位二進制數,左邊補零,從左邊的第一位開始建樹。
B序列的01Trie建好以後,對A序列從1到n每一個數都按位在Trie中遍歷,一開始先設ans爲0,以後優先走與當前本身的二進制位相同的邊,若是沒有則讓ans或上這一位的1。(確保ans儘量地小)
以後輸出這個ans,並將走過的路線所表明的Bi刪除掉就能夠
能夠看出時間複雜度爲
對空間複雜度來講,不要使用滿二叉樹的存儲方式,使用動態開點的方式,空間複雜度不超過
01Trie代碼以下:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int n; int aa[10000005][2] = {{0}}, vv[10000005][2] = {{0}}, co = 1; int A[300005] = {0}; void add(int x) { int o = 1; for (int i = 29; i >= 0; --i) { int bitt = (x >> i) & 1; if (!aa[o][bitt]) { aa[o][bitt] = ++co; } ++vv[o][bitt]; o = aa[o][bitt]; } } int trie(int x) { int o = 1, ans = 0; for (int i = 29; i >= 0; --i) { int bitt = (x >> i) & 1; if (vv[o][bitt]) { --vv[o][bitt]; o = aa[o][bitt]; } else { --vv[o][bitt ^ 1]; o = aa[o][bitt ^ 1]; ans |= (1 << i); } } return ans; } int main() { scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", &A[i]); } for (int i = 1; i <= n; ++i) { int xx; scanf("%d", &xx); add(xx); } for (int i = 1; i < n; ++i) { printf("%d ", trie(A[i])); } printf("%d", trie(A[n])); return 0; }