但前缀树的字符子集比较大的时候,就不适合我之前建立的前缀树了,我们使用左儿子 右兄弟的方式存储Trie。
模板如下:
struct Trie{
int head[maxnode];
int next[maxnode];
int tot[maxnode];
int ch[maxnode];
int sz;
void clears(){head[0]=next[0]=tot[0]=ch[0]=0;sz=1;ans=0;}
void inserts(char *s){
int u=0,v,n=strlen(s);
tot[0]++;
for (int i=0;i<=n;i++){
bool yes=false;
for (v=head[u];v!=0;v=next[v]){
if (ch[v]==s[i]) {
yes=true;
break;
}
}
if (yes==false) {
v=sz;
head[sz]=0;
tot[sz]=0;
next[sz]=head[u];
ch[sz]=s[i];
head[u]=sz;
sz++;
}
u=v;
tot[u]++;
}
}
};
仅供参考;
提供一题例题:uva 11732
计算每个节点通过单词的个数,然后求每个的子节点两两相乘的和n,n乘以此节点到根节点的深度的二倍。将所有节点都这样计算一遍相加和就是答案。(要单独考虑叶子节点的情况)
我的代码:
#include <cstdio>
#include <cstring>
const int maxnode =4000*1000+10;
using namespace std;
struct Trie{
int head[maxnode];
int next[maxnode];
int tot[maxnode];
int ch[maxnode];
int sz;
long long ans;
void clears(){head[0]=next[0]=tot[0]=ch[0]=0;sz=1;ans=0;}
void inserts(char *s){
int u=0,v,n=strlen(s);
tot[0]++;
for (int i=0;i<=n;i++){
bool yes=false;
for (v=head[u];v!=0;v=next[v]){
if (ch[v]==s[i]) {
yes=true;
break;
}
}
if (yes==false) {
v=sz;
head[sz]=0;
tot[sz]=0;
next[sz]=head[u];
ch[sz]=s[i];
head[u]=sz;
sz++;
}
u=v;
tot[u]++;
}
}
void dfs(int depth, int u) {
if(head[u] == 0)
ans += tot[u] * (tot[u] - 1) * depth;
else {
int sum = 0;
for(int v = head[u]; v != 0; v = next[v])
sum += tot[v] * (tot[u] - tot[v]); // 子树v中选一个串,其他子树中再选一个
ans += sum / 2 * (2 * depth + 1); // 除以2是每种选法统计了两次
for(int v = head[u]; v != 0; v = next[v])
dfs(depth+1, v);
}
}
};
struct Trie tree;
int main (){
int n,kase=0;
while (scanf("%d",&n)!=EOF&&n){
char s[1010];
tree.clears();
for (int i=0;i<n;i++){
scanf("%s",s);
tree.inserts(s);
}
tree.dfs(0,0);
printf("Case %d: %lld\n", ++kase, tree.ans);
}
return 0;
}