您的位置:首页 > 其它

UVA - 11732 "strcmp()" Anyone? (字典树的处理)

2016-07-16 19:44 405 查看
题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=28438

1.对于这个题我们可以用字典树来做,我们可以统计前缀相同的字母个数,对于前缀相同的单词每个字母要比较两次(包括‘\0’)。又因为要把任意两个单词相比较。所以相同字母的比较次数为2*val[i[*(val[i]-1).对于不相等的单词,由题意可得这个仅比较一次,即同所得单词相比较次数为(n-val[i])*val[i].对所有的点求和即可。因为这样计数每个都多统计了一次,所以对这个答案要除以2.

2.关于字典树,我们可以用链表来写。也可以用二维数组实现。也可以用左儿子右兄弟的方式来实现。

指针字典树:

struct trie

{

trie *next[28];

int val;

};

trie *root;

inline trie *newnode()//申请字符节点

{

trie *t;

t=(trie*)malloc(sizeof (trie) );

memset(t,0,sizeof (trie) );

return t;

}

void ins(trie *s,char x[])//字典树插入字符

{

int i;

trie *t;

for(i=0;x[i]!='\0';i++)

{

if(s->next[x[i]-'a'])

{

s=s->next[x[i]-'a'];

}

else{

t=newnode();

s->next[x[i]-'a']=t;

s=t;

}

s->val++;//统计项

}

}

二维数组的字典树:

struct trie

{

public:

int ch[maxn][30];//ch储存字符节点

int val[maxn];

int sz=1;

void tt() { sz=1; memset(ch[0],0,sizeof(ch[0]) ); };

int idx(char c) { return c-'a'; }//字符表转换

void insert_(char *s2,int v)//如果该字符不存在于字典树中,则初始化结点。

{

int u=0,n=strlen(s2);

for(int i=0;i<n;i++)

{

int c=idx(s2[i]);

if(!ch[u][c])

{

memset(ch[sz],0,sizeof(ch[sz]));

val[sz]=0;

ch[u][c]=sz++;

}

u=ch[u][c];//向下访问

}

val[u]=v;//统计项

};
}

左儿子右兄弟的表示方法:

class trie

{

public:

int head[maxn],next[maxn],val[maxn],sz;//head[i]表示i结点的左孩子,next[i]表示i节点的右兄弟

char ch[maxn];

void init(){ sz=1,ch[0]=val[0]=head[0]=next[0]=0;}//值为0表示后面没有节点

void ins(char *ss)//字符的插入

{

int v,u=0,len=strlen(ss);

for(int i=0;i<=len;i++)

{

int f=0;

for( v=head[u];v!=0;v=next[v])/*先在当前兄弟里面找到是否有与
s[i]相同的节点,没有则创建一个兄弟节点。有则继续向下遍历。*/

{

if(s[i]==ch[v])

{

f=1;

break;

}

}

if(!f)//没找到与s[i]相等的节点,把这个字符插入到最左边,即这个成为u节点的左儿子。则之前的左儿子就变成v的右兄弟。v没有左二子。

{

v=sz++;

val[v]=0;

ch[v]=s[i];

next[v]=head[u];

head[u]=v;

head[v]=0;

}

u=v;//向下遍历

val[v]++;

}
}

}t;

对于这三种表示方法,用链表动态申请内存很明显要复杂的多。而对于第二种每次申请一个节点都需要将其相应的子节点初始化完。这样初始化既耗费一定的时间。数组的粗存对于内存的占用可能也有消耗(这题用第二种写一直是RE,不知道是怎么回事。然后百度看到了第三种写法,1A)。感觉第三种写法的比上面的还是挺优化的了。在空间时间上都存在优化了。

AC:

#include<algorithm>

#include<iostream>

#include<cmath>

#include<map>

#include<string.h>

#include<cstring>

#include<vector>

#include<queue>

#include<stdio.h>

using namespace std;

#define ll long long int

#define maxn 8000008

const int mod=20071027;

char s[maxn];

class trie

{

public:

int head[maxn],next[maxn],val[maxn],sz;

char ch[maxn];

void init(){ sz=1,ch[0]=val[0]=head[0]=next[0]=0;}

void ins(char *ss)

{

int v,u=0,len=strlen(ss);

for(int i=0;i<=len;i++)

{

int f=0;

for( v=head[u];v!=0;v=next[v])

{

if(s[i]==ch[v])

{

f=1;

break;

}

}

if(!f)

{

v=sz++;

val[v]=0;

ch[v]=s[i];

next[v]=head[u];

head[u]=v;

head[v]=0;

}

u=v;

val[v]++;

}

}

}t;

int main(void)

{

int n;

ll ans;

int k=0;

while(scanf("%d",&n)!=EOF)

{

t.init();

if(n==0) break;

ans=0;

ll sum1=0;

ll sum2=0;

for(int i=0;i<n;i++)

{

scanf("%s",s);

t.ins(s);

}

for(int i=1;i<t.sz;i++)

{

sum1+=t.val[i]*(t.val[i]-1);

if(t.ch[i]=='\0')

sum2+=(n-t.val[i])*t.val[i] ;

}

printf("Case %d: %lld\n",++k,sum1+sum2/2);

}

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