今天听学长讲了一下字典树,收获颇丰。
举个例子,给你n个单词,每个单词的长度最大不超过len,给的顶一个长为length的长字符串,问这个字符串的前缀里包含多少个所给的单词。
先分析这道题,需要建立一个字典树将所有的单词都放到一个树里(可以看成一个不满的26叉树,每一层都是字母a~z),然后用长文本去暴力匹配这个树,记录个数。
PS:遇到题目描述中出现总单词的总长度不超过……;
或者,给定……个单词,每个单词不超过……的长度等等。
诸如此类的描述,都可以往这方面想。
字典树的操作包括建树和查找两个操作。
1.建树:
建立一个int类型二维数组ch[u][id]代表的值是当前字母的编号。其中第一维的u代表它父亲节点的编号,id代表的是哪个字母,可以写成字母的ASCII值也可以写成0~25.
int ch[n*len][26];///n为单词个数,len为单词长度
int num[n*len];///可以用来储存当前节点的子树所包含的单词数
///也可以记录一个单词的末位置,
///具体情况具体分析,此题未采用第二种;
int cnt;///用来记录字符编号
char a[len];
char s[length];
void init()
{
memset(ch[0],0,sizeof(ch[0]));///只初始化第一层,避免超时
cnt=0;
}
void setup(char *a,int len)
{
int u=0;
for(int i=0;i<len;i++)
{
int id=a[i]-'a';
if(ch[u][id]!=0)
u=ch[u][id];
else
{
cnt++;
ch[u][id]=cnt;
///num[cnt]++;///记录每个节点的子树包含多少单词
memset(ch[cnt],0,sizeof(0));///用到那一层就初始化哪一层,防止超时
u=ch[u][id];
}
}
num[u]++;///插入完成之后记录末位置
}
举个例子:
字典树
字典树每个节点是每个单词中的一个个字母。
2.查找:
用长文本字符串去遍历字典树。
int solve(char *s,int length)
{
int u=0,ans=0;
for(int i=0;i<length;i++)
{
int id=s[i]-'a';
if(ch[u][id]==0)
return ans;///遍历到头了,往下再也没有元素,直接返回答案
else
{
u=ch[u][id];
ans+=num[u];///未遍历到头,继续遍历,加上每个位置的单词数量
}
}
return ans;
}