您的位置:首页 > 其它

字典树模板

2013-04-23 22:02 232 查看
struct Trie  //字典树结构
{
Trie *child[26];
int num;   //子节点数
bool end;  //判断该字母是否为某个单词的结束字母
Trie() //构造函数
{
num=0;
end=0;
memset(child,0,sizeof(child));
}
};
Trie *root,*s,*lrelia;
void Create(char *str)  //插入单词
{
s=root;
int i=0;
while(str[i])
{
int id=str[i]-'a';
if(s->child[id]==0) //如果该字母还没有出现在字典中
{
s->child[id]=new Trie;
s->num++; //子节点数+1
s=s->child[id];
}
else
{
s=s->child[id];
}
i++;
}
s->end=1;//标记单词的最后一个字母
}
double Search(char *str)
{
s=root;
double len=1; //因为第一个字母必须得敲击,所以len赋值1,
int o=strlen(str);
/*
既然前一个字母的子节点数决定下一个字母是否要敲击,
我们只需要遍历第1个到len-1个字母就可以了。
*/
for(int i=0;i<o-1;++i)
{
int id=str[i]-'a';
s=s->child[id];
if(s->num>1||s->end==1) len++;
}
return len;
}


上面的模板是动态的,优点:节约空间,代码简洁,缺点:太慢了,比如POJ3630 Phone list,动态模板和静态模板相差了10倍!

静态模板:

#define mod 20071027
#define MAXN 400005
int dp[5555],num,re,p;
char a[300005];
struct Trie
{
int child[26];
bool end;
Trie()
{
end=0;
memset(child,0,sizeof(child));
}
void set()
{
end=0;
memset(child,0,sizeof(child));
}
}t[MAXN];
void Create(char *s)
{
int root=0,i=0,id;
while(s[i])
{
id=s[i]-'a';
if(t[root].child[id]==0)
t[root].child[id]=p++;
root=t[root].child[id];
i++;
}
t[root].end=1;
}
void Search(char *s)
{
int root=0,i=0,id,len=strlen(s);
for(i=0;i<len;++i)
{
id=s[i]-'a';
if(t[root].child[id])
{
root=t[root].child[id];
if(t[root].end)
{
num+=dp[re+i+1];
}
}
else break;
}
}


更高效的静态模板:

#define mod 20071027
#define MAXN 300005
int dp[MAXN];
char a[MAXN];
struct Trie
{
int ch[MAXN][26];
bool val[MAXN];
int sz;
void clear()
{
sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
void Create(char *s)
{
int u=0,n=strlen(s); //u相当于根节点
for(int i=0;i<n;++i)
{
int c=s[i]-'a';
if(!ch[u][c])
{
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
val[u]=1;
}
void Search(char *s,int pos)
{
int i=0,u=0;
while(s[i]!=0&&ch[u][s[i]-'a'])
{
u=ch[u][s[i]-'a'];
if(val[u])
{
dp[pos]=dp[pos+i+1]?dp[pos]+dp[pos+i+1]:dp[pos];
if(dp[pos]>=mod)
dp[pos]-=mod;
}
++i;

}
}
}tr;


o(︶︿︶)o GO DIE,居然还有前向星式的模板,一维的

前向星式图解:



/*
题意: 给出n个字符串, 计算两两比较的次数. 每次比较都需要比较(str1[i] == str2[i])和 (str1[i] == '\0'各一次).
点评:
将N个字符串插入前缀树,‘\0’也插入,这样就能区分字符串的终结点
S1与S2的共同前缀S,则比较次数为len(S)*2+1
但S1与S2相同,则比较次数为 (len(S1)+1)*2 (注意:这时连'\0’都算进去了噢~,因为适用性,所以模板最好是用一维的前向星式的
*/
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define MAX 1005
const int maxnode = 4001*1000+5;
const int sigma_size = 26;
typedef long long ll;
struct Trie
{
int first[maxnode], next[maxnode]; //前向星式子建图还记得么,亲?
int total[maxnode], ch[maxnode]; //total[i]记录每个节点被遍历的的次数,ch[i]记录字符串的值的
int sz;  //记录ch的坐标的
void clear()
{
sz = 1;
total[0] = first[0] = next[0] = 0;
}

void insert(const char *s)
{
int u = 0, v, n = strlen(s);
total[u]++;
for(int i = 0; i <= n; ++i)
{
bool flag = false;
for(v = first[u]; v != 0; v = next[v]) //前向星式遍历
{
if(ch[v] == s[i])
{
flag = true;
break;
}
}
if( !flag )
{
v = sz++;
total[v] = 0; //初始化
ch[v] = s[i];
next[v] = first[u];
first[u] = v;
first[v] = 0;
}
u = v;
total[u]++;
}
}

void dfs(int depth, int u, ll &ans)
{
if( first[u] == 0 ) ans += total[u]*(total[u]-1)*depth; //如果下面没有节点了,那么ans*=total[u]*(total[u]-1)*depth
else
{
int sum = 0;
for(int v = first[u]; v != 0; v = next[v])
sum += total[v]*(total[u]-total[v]);  //否则计算比较次数,当前节点-相同的分支的节点的数量
ans += (sum/2*(2*depth+1));
for(int v = first[u]; v != 0; v = next[v])
dfs(depth+1, v, ans);
}
}
};
int n;
char str[MAX];
Trie tr;
int main()
{
int t = 1;
while(~scanf("%d", &n))
{
if(n == 0) break;

tr.clear();
for(int i = 0; i < n; ++i)
{
scanf("%s", str);
tr.insert(str);
}

ll ans = 0;
tr.dfs(0, 0, ans);
printf("Case %d: %lld\n", t++, ans);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: