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);
}
}
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);
}
}
相关文章推荐
- Linux下软件的安装方法
- Codeforces 602B Approximating a Constant Range
- JavaWeb核心编程之(三.3)Servlet Init 配置
- hibernate学习 hibernate-tutorials(二)——annotations
- 分布式中使用Redis实现Session共享
- CodeForces 697B Barnicle 和 Codeforce 691 C Puzzles 科学计数法的正逆互推 CodeForces 691B 一个对称的小题;
- 深入解析String#intern
- MySQL索引分析
- linux
- extjs tree节点展开后无法关闭的问题
- leetcode_c++:链表:Remove Duplicates from Sorted List II(082)
- python基础
- c语言中i++,++i的计算
- UE4入门与精通
- 事件监听器
- Spark 1.6.2 + Hadoop 2.7.2 集群搭建
- Extjs怎么使用一个Extjs控件
- Codeforces 591B Rebranding
- 前端工作中一些关于hosts的简单设置
- popuwindow