今天看的字典树,一开始就理解歪了,怎么看怎么不懂。
一开始看的是动态模板,那个指针跟指针相关的函数,当时C++学的时候就了解的不深,直接看不懂。看了一会之后放弃,开始看静态模板。虽然内存消耗挺大,但是在没办法。有几个问题想了好久才理解。
1、字典树的根节点为0,不存放任何字母,仅用于把所有的字典树连在一起。
2、将字母转化为数字存放在节点中,也可以把它当做边的权值。
3、在不同解法中,insert构建字典树的方法不同,val数组代表的意义也不同。
在当理解静态模板时,又看了下一道题,根据题意的要求用的,不同的insert写法。我当时就单纯的以为val数组的意义是一样的,怎么看怎么不对,又晕乎了。后来发现根本是不同的。
经典的模板题
HDU 1251 统计难题
找出所有以字符串s为前缀的单词个数。
#include<bits/stdc++.h>
using namespace std;
struct Trie
{
int ch[1000005][26];//第i个节点的第j个儿子的存储位置
int val[1000005];
int sz;
void Clear()
{
sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
int idx(char c)
{
return c-'a';
}
void Insert(string s)
{
int u=0,n=s.size();
for(int i=0;i<n;i++)
{
int c=idx(s[i]);
if(!ch[u][c])//暂时不存在该子节点
{
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][c]=sz++;
}
val[u]++;//以某一字符串为前缀单词的数量 当前节点的子节点有多少个
u=ch[u][c];
}
val[u]++;
}
int Find(string s)
{
int u=0,n=s.size();
for(int i=0;i<n;i++)
{
int c=idx(s[i]);
if(!ch[u][c])
return 0;
u=ch[u][c];
}
return val[u];
}
};
Trie T;
int main()
{
string s;
T.Clear();
while(getline(cin,s))
{
if(s=="")
break;
T.Insert(s);
}
while(getline(cin,s))
{
printf("%d\n",T.Find(s));
}
return 0;
}
直接的利用数组的写法,本质相同,但感觉这个比较好理解
#include <bits/stdc++.h>
using namespace std;
int trie[1000010][26]; //数组形式定义字典树,值存储的是下一个字符的位置
int num[1000010]={0}; //附加值,以某一字符串为前缀的单词的数量
int pos = 1;
void Insert(char word[]) //在字典树中插入某个单词
{
int i;
int c = 0;
for(i=0;word[i];i++)
{
int n = word[i]-'a';
if(trie[c][n]==0) //如果对应字符还没有值
trie[c][n] = pos++;
c = trie[c][n];
num[c]++;
}
}
int Find(char word[]) //返回以某个字符串为前缀的单词的数量
{
int i;
int c = 0;
for(i=0;word[i];i++)
{
int n = word[i]-'a';
if(trie[c][n]==0)
return 0;
c = trie[c][n];
}
return num[c];
}
int main()
{
char word[11];
while(gets(word))
{
if(word[0]==NULL) //空行。gets读入的回车符会自动转换为NULL。
break;
Insert(word);
}
while(gets(word))
printf("%d\n",Find(word));
return 0;
}
另一种insert方法,在插入过程中,val数组值为0,表示该点不是单词的末字母。val数组值不为0,代表以该字母为末端的单词的长度。
void insert(char *s)
{
int u=0,n=strlen(s);
for(int i=0;i<n;i++)
{
int id=idx(s[i]);
if(ch[u][id]==0)//无该儿子
{
ch[u][id]=sz;
memset(ch[sz],0,sizeof(ch[sz]));
val[sz++]=0;//初始化,先假设该节点不为叶节点,不代表一个单词的结束
//cout<<"!"<<sz-1<<" "<<val[sz-1]<<endl;
}
u=ch[u][id];
}
val[u]=n;//u:当前添加分支的叶节点的点编号,也就是要添加单词的最后一个字母填在树中的编号
//val[u]:以被点编号u标记的字母为最后一个字母的单词的长度
//cout<<u<<" "<<val[u]<<endl;
}