字典樹(trie)的模板題
阿新 • • 發佈:2019-01-26
一口氣看了3集《Lady~》終於把拖了半年的爛尾樓給完工了~~一看3:40了...再一想~~擦~~今天整了一天的題還沒整出來~~好吧~~AC了....寫完睡..
搞Trie是因為看了Mtrix67關於矩陣乘法的日誌中提到的AC自動機...AC自動機又必須熟悉Trie和KMP...KMP沒啥問題..Trie沒接觸過..就所幸熟悉了下...
其實這個字典樹也很簡單...結構上來說是棵樹...除了根節點..樹上的每個點都代表著一個字母...從第二層出發(根節點不存資料)..每條由葉子節點路徑上的點依次組成的字串即為一個相當於字典裡的單詞(但不一定所有的單詞都是第二層走到葉子,如果字典中的有包含關係...則可能從第二層到中間某點代表一個單詞)...Trie裡每個節點以及其孩子代表單詞表裡有當前位置是這個節點的字母下個位置是其孩子的字母的單詞.....如用 akb ab abc air oricon orish 所構造出來的字典樹為:
我覺得字典樹關鍵的不是樹的理解和樹的意義....關鍵是如何來建樹..首先是每個節點的定義
struct node
{
node *a[27];
int p;
}
樹上的每個點這樣定義...因為每個點後面最多可能跟26個字母~~所以開個27廠的指標..這裡用指標是因為這個建樹每個點下到底會接上多少個是不清楚的而且差距比較大...而用動態來管理顯然很節約空間也方便操作....這裡的p是關於這道題而設定的..
建樹過程...其實每個點先給26個指標就是判斷很方便...如果下一個點前面已經開闢過了...就遞迴進去...如果沒有~~那就開闢新點~~
回到這道題..是求要用最短的字元來代表字典中的單詞.....這裡解釋用p的意思....我這裡的每個點的p代表在建樹時有多少個單詞經過了這個點....那麼再輸出時...如果只有一個單詞經過這個點..顯然從第二層到這個點已經足夠表示其單詞了...但也要注意..或許一個單詞走完了...所有點都是p>1的..那就只能用這個單詞的全部來代表它~void make(int k,node *h) { h->p++; if (k>l) return; if (h->a[s[n][k]-'a']!=NULL) make(k+1,h->a[s[n][k]-'a']); else { int i,j; for (i=k;i<=l;i++) { t=new node; t->p=1; for (j=0;j<26;j++) t->a[j]=NULL; h->a[s[n][i]-'a']=t; h=t; } } return; }
Program:
#include<iostream>
using namespace std;
struct node
{
node *a[27];
int p;
}*t,*h;
char s[1011][35];
int n,i,j,l;
void make(int k,node *h)
{
h->p++;
if (k>l) return;
if (h->a[s[n][k]-'a']!=NULL) make(k+1,h->a[s[n][k]-'a']);
else
{
int i,j;
for (i=k;i<=l;i++)
{
t=new node;
t->p=1;
for (j=0;j<26;j++) t->a[j]=NULL;
h->a[s[n][i]-'a']=t;
h=t;
}
}
return;
}
void find(int k,int m,node *h)
{
printf("%c",s[m][k]);
if (k==strlen(s[m])-1 || h->p==1) return;
find(k+1,m,h->a[s[m][k+1]-'a']);
}
int main()
{
n=0; h=new node; h->p=0;
for (i=0;i<26;i++) h->a[i]=NULL;
while (~scanf("%s",s[++n]))
{
l=strlen(s[n])-1;
make(0,h);
}
n--;
for (i=1;i<=n;i++)
{
printf("%s ",s[i]);
find(0,i,h->a[s[i][0]-'a']);
if (i!=n) printf("\n");
}
return 0;
}