835. Trie字串統計
模板題:
維護一個字串集合,支援兩種操作:
I x
向集合中插入一個字串 x;Q x
詢問一個字串在集合中出現了多少次。
共有 N 個操作,所有輸入的字串總長度不超過 10^5,字串僅包含小寫英文字母。
輸入格式
第一行包含整數 N,表示運算元。
接下來 N 行,每行包含一個操作指令,指令為 I x
或 Q x
中的一種。
輸出格式
對於每個詢問指令 Q x
,都要輸出一個整數作為結果,表示 x 在集合中出現的次數。
每個結果佔一行。
資料範圍
1≤N≤2∗10^4
輸入樣例:
5
I abc
Q abc
Q ab
I ab
Q ab
輸出樣例:
1
0
1
個人理解:
個人感覺這個Trie是比較簡單理解的,直接給上注意點還有圖解;
AC程式碼:
#include <iostream>
using namespace std;
//定義:不超過 10^5;
//符合要求即可;
const int Ma=100010;
//這裡的26是a~z(字串僅包含小寫英文字母)
//con[Ma]是記錄Trie每個父節點最後一個的子節點(有圖解);
//idx是記錄c的“下標”;
int Trie[Ma][26],con[Ma],idx; //這裡容易誤解的點:26 a~z並不是層級;而是以a~z帶頭的父節點;
char str[Ma];
//這是構造Trie樹;
void insert(char str[]){
int p=0;
for(int i=0;str[i];i++){
int c=str[i]-'a';
//如果沒有記錄這個字元,idx開闢一個位置來兜住;
if(!Trie[p][c]){
Trie[p][c]=++idx;
}
//p來指向下一個;
p=Trie[p][c];
}
//記錄最後一個字元的位置的個數; 比如 abcd 記錄一次,abcd 再來一次 con[p]=2;
con[p]++;
}
//查詢
int search(char str[]){
int p=0;
for(int i=0;str[i];i++){
int c=str[i]-'a';
//按順序找下去,不對直接返回0;
if(!Trie[p][c]){
return 0;
}else{
//指向下一個;
p=Trie[p][c];
}
}
//返回結果;
return con[p];
}
int main(){
int n;
cin >> n;
while(n--){
char c;
cin >> c >> str;
if(c=='I'){
insert(str);
}else{
cout << search(str) << endl;
}
}
return 0;
}
圖解:來源--------AC 四谷夕雨
解疑:
Trie[p][c]=++idx; //這是什麼?idx幹什麼用的?
- 一維下標:父節點的位置
二維下標:當前節點的位置(az->025)
值:當前節點的id(用idx標識,唯一性) - 這裡是可以自己圖畫手推的:手推也可能就會發現用這種方法的話,是會浪費一層裡的空間的.......這就自定義想了,在下面我會給出一些例題,是很明顯的吧,有最佳化可以q我
- 自己手推很重要,本人在沒手推之前,總是覺得字元之間會重複什麼的.......,手推才意識到idx的強大,idx是即將待操作的結點下標 (看自己的理解吧);
- 哦對,在這以前要知道怎麼構建Trie,要不然夠嗆,比如abcd 的“路徑”是可以儲存 abc的只需記錄最後一個節點;