ceph-deploy部署
阿新 • • 發佈:2020-09-07
trie 樹,又叫字典樹,字首樹,可以用來求查詢這個字串的數量,或者以這個字串為字首的字串的數量。
trie 樹用了一個字首的思想,就是把每個字串拆一位位拆開來,然後存在樹的邊上,比如有 cup,apple,cake,app,blog 這幾個單詞,那麼我們就可以構建這樣一棵有根樹:
但是,在樹上邊是很不好維護的,於是我們可以把他放在這條邊的兩個端點中,深度較大的那個點上,像這樣:
但這樣還沒完,我們如何判斷這是一個完整字串呢?比如,你如何知道 ca 是不是一個完整的字串呢?是判斷最後一個字元是否為葉子節點嗎?明顯不是,上面的例子中,app的最後一個字元就不是葉子節點。那怎麼辦呢?直接給節點打個標記嘛~
於是,我們知道了什麼是 trie 樹,接下來我們要解決插入和查詢的問題。
我們可以開一個數組,用 \(trie_{i,j}\)
x-'a'
來表示字元 x。然後,對於插入和查詢操作,我們只要列舉字串的每一位,然後用一個變數
rt
來記錄當前節點的編號。然後,對於第 i 個字元,我們只要查詢它是否存在,也就是 \(trie_{rt,s[i-1]}\) 是否為 \(0\)(字串下標從 \(0\) 開始),如果為 \(0\),若是插入,則加入這個節點;若是查詢,則返回 false。程式碼如下:
void insert(string s) { int len=s.length(),root=0;//root即上面的rt for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) trie[root][id]=++tot;//tot記錄節點數 root=trie[root][id]; } v[root]=true;//v就是用來記錄是否為字串結尾的、 return ; } bool find(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return false; root=trie[root][id]; } if(v[root]) return true; else return false; }
然後,上面說到我們可以查詢以這個字串為字首的字串有多少,怎麼弄呢?我們可以用 \(sum_i\) 表示有從根節點到 \(i\) 這個字串(trie 樹上從根節點到一個節點的路徑其實就是一個字串的字首)字首的字串的數量(有點繞、),然後每次插入走到一個節點就 sum[i]++
即可,而查詢則和上面的一樣,最後返回末尾節點的 sum 即可。
程式碼實現如下:
int fin_pre(string s) { int len=s.length(),root=0; for(int i=0;i<len;i++) { int id=s[i]-'a'; if(trie[root][id]==0) return 0; root=trie[root][id]; } return sum[root]; }
整體程式碼實現如下:
#include<cstdio>
#include<string>
#include<iostream>
using namespace std;
int n,m;
int tot,trie[1000005][26],sum[1000005];
bool v[1000005];
void insert(string s)
{
int len=s.length(),root=0;
for(int i=0;i<len;i++)
{
int id=s[i]-'a';
if(trie[root][id]==0) trie[root][id]=++tot;
root=trie[root][id];
sum[root]++;
}
v[root]=true;
return ;
}
bool find(string s)
{
int len=s.length(),root=0;
for(int i=0;i<len;i++)
{
int id=s[i]-'a';
if(trie[root][id]==0) return false;
root=trie[root][id];
}
if(v[root]) return true;
else return false;
}
int find_pre(string s)
{
int len=s.length(),root=0;
for(int i=0;i<len;i++)
{
int id=s[i]-'a';
if(trie[root][id]==0) return 0;
root=trie[root][id];
}
return sum[root];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
insert(s);
}
for(int i=1;i<=m;i++)
{
int opt;
scanf("%d",&opt);//opt=1表示查詢這個字串是否存在,opt=2表示查詢包含這個字串字首的字串的數量
string s;
cin>>s;
if(opt==1) printf("%s\n",find(s)?"Yes":"No");
else printf("%d\n",find_pre(s));
}
return 0;
}
由於沒有找到合適的模板題所以無法測驗,但 insert 和 find 函式是沒問題的,find_pre 也不會出太大的問題