1. 程式人生 > 實用技巧 >ceph-deploy部署

ceph-deploy部署

trie 樹,又叫字典樹,字首樹,可以用來求查詢這個字串的數量,或者以這個字串為字首的字串的數量。

trie 樹用了一個字首的思想,就是把每個字串拆一位位拆開來,然後存在樹的邊上,比如有 cup,apple,cake,app,blog 這幾個單詞,那麼我們就可以構建這樣一棵有根樹:

但是,在樹上邊是很不好維護的,於是我們可以把他放在這條邊的兩個端點中,深度較大的那個點上,像這樣:

但這樣還沒完,我們如何判斷這是一個完整字串呢?比如,你如何知道 ca 是不是一個完整的字串呢?是判斷最後一個字元是否為葉子節點嗎?明顯不是,上面的例子中,app的最後一個字元就不是葉子節點。那怎麼辦呢?直接給節點打個標記嘛~
於是,我們知道了什麼是 trie 樹,接下來我們要解決插入和查詢的問題。
我們可以開一個數組,用 \(trie_{i,j}\)

表示節點 i 的兒子中,是字元 j 的節點的編號。當然,我們不能用字元做下標,但是我們可以把他轉成數字,比如題目說明只會出現 a~z 的字元,那麼我們可以用 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 也不會出太大的問題