UVA 11732 strcmp() Anyone?(字典树Trie)
题意:给你N个单词,要你求这N个单词两两之间进行比较一共要比较多少次.用的是strcmp函数实现的比较.
分析:详解见刘汝佳:训练指南P210.结合下面的图来看代码:
假设现在有单词tha,thb,bd,bd已经建好了Trie树,现在要如何计算比较次数呢?比较次数计算如下图:
从上面图可以看出,一个节点有分叉的时候才能共享比较次数出来,如果比如thaaaac与thaaaab 在他们都走到c或b的时候,他们走的是同一条路,那个时候是不比较的.只有当他们要分道扬镳的时候,就可以做比较了.
那么对于相同的单词如何比较呢?首先我们在程序中把字符串的尾’\0’也加入了Trie中,所以如果一个单词不可能出现在非叶节点,单词只能出现在叶节点,当叶节点的tot值>1的时候,它就肯定有重合单词了.且此时的计算公式是:tot*(tot-1)/2 * (2*(depth-1)+2).即C[tot][2]*2*depth.代码:
void dfs(int depth, int u) {
if(head[u] == 0) // 叶结点,u是叶节点,但是u该点可能重合了多个相同的单词,所以
//tot[u]>=1
ans += tot[u] * (tot[u] - 1) *depth;//其实应该是 tot[u]*(tot[u] - 1)/2 * (2*depth)
AC代码:
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int maxnode=4000*1000+1000;
struct Trie
{
int head[maxnode];
int next[maxnode];
int tot[maxnode];
char ch[maxnode];
int sz;
long long ans;
void clear()
{
ans=0;
sz=1;
head[0]=next[0]=tot[0]=0;
}
void insert(char *s)
{
int n=strlen(s),u=0;
tot[u]++;
for(int i=0;i<=n;i++)//s[n]='\0'也要插入Trie
{
bool found=false;
for(int j=head[u];j;j=next[j])
if(ch[j]==s[i])
{
u=j;
found=true;
break;
}
if(!found)
{
next[sz]=head[u];
head[u]=sz;
head[sz]=0;
tot[sz]=0;
ch[sz]=s[i];
u=sz++;
}
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;v=next[v])
sum+=tot[v]*(tot[u]-tot[v]);
ans+= sum/2*(depth*2+1);
for(int v=head[u];v;v=next[v])
dfs(depth+1,v);
}
}
long long count()
{
ans=0;
dfs(0,0);
return ans;
}
};
Trie trie;
char word[1000+100];
int main()
{
int kase=0;
int n;
while(scanf("%d",&n)==1&&n)
{
trie.clear();
for(int i=0;i<n;i++)
{
scanf("%s",word);
trie.insert(word);
}
printf("Case %d: %lld\n",++kase,trie.count());
}
return 0;
}