字典树模板( 指针版 && 数组版 )
模板 :
#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 26;
struct Trie
{
Trie *Next[maxn];
int v;
inline void init(){
this->v = 1;
for(int i=0; i<maxn; i++)
this->Next[i] = NULL;
}
};
Trie *root = (Trie *)malloc(sizeof(Trie));
void CreateTrie(char *str)
{
int len = strlen(str);
Trie *p = root, *tmp;
for(int i=0; i<len; i++){
int idx = str[i]-'a';
if(p->Next[idx] == NULL){
tmp = (Trie *)malloc(sizeof(Trie));
tmp->init();
p->Next[idx] = tmp;
}else p->Next[idx]->v++;
p = p->Next[idx];
}
p->v = -1;//若为结尾,则将v改成-1,当然字典树里面的变量都是要依据题目
//设置并且在特定位置赋值的
}
int FindTrie(char *str)
{
int len = strlen(str);
Trie *p = root;
for(int i=0; i<len; i++){
int idx = str[i]-'a';
p = p->Next[idx];
//...进行一系列操作
}
}
inline void DelTrie(Trie *T)
{
if(T == NULL) return ;
for(int i=0; i<maxn; i++){
if(T->Next[i] != NULL)
DelTrie(T->Next[i]);
}
free(T);
return ;
}
int main(void)
{
root.init();//!!!
//...
}
#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 26 + 1;
struct Trie
{
int Next[maxn], v;
inline void init(){
v = 1;
memset(Next, -1, sizeof(Next));
}
};
struct Trie Node[1000000]; // 字典树可能最多的节点数,注意开足了!
int tot = 0;
void CreateTrie(char *str)
{
int len = strlen(str);
int now = 0;
for(int i=0; i<len; i++){
int idx = str[i]-'a';
int nxt = Node[now].Next[idx];
if( nxt == -1){
nxt = ++tot;
Node[nxt].init();
Node[now].Next[idx] = nxt;
}else Node[nxt].v++;
now = nxt;
}
// Node[now].v = //尾部标志
}
int FindTrie(char *str)
{
int len = strlen(str);
int now = 0;
int nxt;
for(int i=0; i<len; i++){
int idx = str[i]-'a';
if(Node[now].Next[idx] != -1) now = Node[now].Next[idx];
else return 0;
}
return Node[now].v;//返回该返回的值
}
字典树算法参考 ==> http://www.cnblogs.com/tanky_woo/archive/2010/09/24/1833717.html
IOI论文《浅析字母树在信息学竞赛中的应用》 ==> http://www.doc88.com/p-434727490439.html
相关题目 :
① HUD 1251
题意 : 给出很多单词(只有小写字母组成,不会有重复的单词出现),要求统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
分析 : 模板题,直接累加前缀即可
///数组版
#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 26 + 1;
struct Trie
{
int Next[maxn], v;
inline void init(){
v = 1;
memset(Next, -1, sizeof(Next));
}
};
struct Trie Node[1000000]; // 字典树可能最多的节点数,注意开足了!
int tot = 0;
void CreateTrie(char *str)
{
int len = strlen(str);
int now = 0;
for(int i=0; i<len; i++){
int idx = str[i]-'a';
int nxt = Node[now].Next[idx];
if( nxt == -1){
nxt = ++tot;
Node[nxt].init();
Node[now].Next[idx] = nxt;
}else Node[nxt].v++;
now = nxt;
}
}
int FindTrie(char *str)
{
int len = strlen(str);
int now = 0;
int nxt;
for(int i=0; i<len; i++){
int idx = str[i]-'a';
if(Node[now].Next[idx] != -1) now = Node[now].Next[idx];
else return 0;
}
return Node[now].v;
}
char s[11];
int main(void)
{
Node[0].init();
while(gets(s)){
if(s[0] == '\0') break;
CreateTrie(s);
}
while(scanf("%s", s)!=EOF){
printf("%d\n",FindTrie(s));
}
return 0;
}
///指针版
#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 26;
struct Trie
{
Trie *Next[maxn];
int v;
inline void init(){
this->v = 1;
for(int i=0; i<maxn; i++)
this->Next[i] = NULL;
}
};
Trie *root = (Trie *)malloc(sizeof(Trie));
inline void CreateTrie(char *str)
{
int len = strlen(str);
Trie *p = root, *tmp;
for(int i=0; i<len; i++){
int idx = str[i]-'a';
if(p->Next[idx] == NULL){
tmp = (Trie *)malloc(sizeof(Trie));
tmp->init();
p->Next[idx] = tmp;
}else p->Next[idx]->v++;
p = p->Next[idx];
}
//p->v = -1;
}
int FindTrie(char *str)
{
int len = strlen(str);
Trie *p = root;
for(int i=0; i<len; i++){
int idx = str[i]-'a';
if(p->Next[idx] != NULL) p = p->Next[idx];
else return 0;
}
return p->v;
}
inline void DelTrie(Trie *T)
{
if(T == NULL) return ;
for(int i=0; i<maxn; i++){
if(T->Next[i] != NULL)
DelTrie(T->Next[i]);
}
free(T);
return ;
}
char s[11];
int main(void)
{
root->init();
while(gets(s)){
if(s[0] == '\0') break;
CreateTrie(s);
}
while(scanf("%s", s)!=EOF){
printf("%d\n",FindTrie(s));
}DelTrie(root);
return 0;
}
② HDU 2846
题意 : 给出 P 个单词和 Q 个询问,对于每一次询问给出询问串是多少个 P 中字符的子串
分析 : 利用KMP的话可以做,但是复杂度爆炸,可以利用字典树,将 P 个单词的每一个以及其子串全部丢去建树,最后问询串直接去跑字典树查询即可。但是在字典树上权值的累加需要注意,例如 abb 这个串,在分解的时候 b 即属于 bb 也属于 b 那么当问询 b 这个字符串的时候就应该输出 2 吗?很显然不是,对于当前分解的串,我们需要给它加一个 id 来判断当前的字符是否是由同一个串分解而来的,就能避免重复计算。
#include<string.h> #include<stdio.h> #include<algorithm> #include<malloc.h> using namespace std; const int maxn = 26; struct Trie{ Trie *Next[maxn]; int v, who; }; Trie *root = (Trie *)malloc(sizeof(Trie)); void CreateTrie(char *str, int who) { int len = strlen(str); root->who = who; for(int st=0; st<=len-1; st++){ Trie *p = root, *tmp; for(int i=st; i<len; i++){ int id = str[i] - 'a'; if(p->Next[id]==NULL){ tmp = (Trie *)malloc(sizeof(Trie)); tmp->v = 1; tmp->who = who; for(int j=0; j<maxn; j++) tmp->Next[j] = NULL; p->Next[id] = tmp; p = p->Next[id]; }else{ if(who != p->Next[id]->who) { p->Next[id]->v++; p->Next[id]->who = who; } p = p->Next[id]; } } } } int ans = 0; bool TrieFind(int n, char *str) //代码将演示在树上找str的前缀是否存在 { ans = 0; int len = strlen(str); Trie *p = root; for(int i=0; i<len; i++){ int id = str[i] - 'a'; if(p->Next[id] != NULL) p = p->Next[id]; else return false; } ans = p->v; return true; } inline void DelTrie(Trie *T) { int i; if(T == NULL) return ; for(int i=0; i<maxn; i++){ if(T->Next[i] != NULL){ DelTrie(T->Next[i]); } } free(T); return ; } char str[100]; int main(void) { for(int i=0; i<maxn; i++) root->Next[i] = NULL; root->who = -1; root->v = 0; int n, q; scanf("%d", &n); for(int i=0; i<n; i++){ scanf("%s", str); CreateTrie(str, i); } scanf("%d", &q); for(int i=0; i<q; i++){ scanf("%s", str); if(TrieFind(n, str)) printf("%d\n", ans); else puts("0"); } DelTrie(root); return 0; }