UVA 11732 链式字典树

题意

给一些字符串,问如果使用strcmp函数对这些字符串两两比较,需要进行多少次字符比较。(strcmp函数在题目中已给出,如果字符相等,则还需要将该字符与’\0’进行比较)

题解

链式字典树我自己的叫法,网上大多数题解都把这种字典树叫做左儿子,右兄弟的字典树,但是这种叫法感觉会引起误解。一开始我以为右兄弟是根的兄弟,后来才意识到右兄弟是左儿子的兄弟。
具体效果见下图。
链式字典树
可以看到,这样存储只需要开节点个数大小的数组就可以了,不需要再开一个二维数组,从而节省了大量空间。对于这道题来说,所需的数组空间直接降了1.5个数量级,效果是非常明显的。
至于链式字典树的存储方式,可以用链表,也可以用数组模拟链表。这里我选择用数组模拟链表来避免指针操作。

v=sz++;
c[v]=st[i];
val[v]=0;
son[v]=0;
nex[v]=son[u];
son[u]=v;

上述代码是链式字典树增加节点的过程,由于链式字典树本身的数组无法记录节点对应的字符信息,所以需要增加一个字符记录数组,也就是C。val用来记录节点值,在本题中用来记录访问次数。nex对应上图中的next,son对应上图中的son1。
通过链式字典树解决了存储空间的问题以后,本题就算解决一半了。另一半是统计比较次数。对于普通字符(除了’\0’以外),每次字符比较都会比较两次,调用字符比较的次数就是val[u]值(u表示将字符串插入字典树过程中访问的节点)。如果一直比较到结尾还没有分出大小,那么还会额外针对’\0’比较两次,这时候就需要+cnt[u]。(u代表末尾节点)如果在比较过程中分出了大小,还需要再加1次,因为最后一次不相等的比较没有计算。
最后将所有计算得到的值累加起来就是所求的总比较次数。

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#include<stack>
#include<string>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f
#define MAXN 100010
#define MOD 1000000009
#define EPS 1e-10
using namespace std;

int ch[4000010];
int val[4000010],nex[4000010],son[4000010],cnt[4000010];
char c[4000010];
char st[1010];
int sz;
LL ans;

int getNum(char c) {
    if(c>='0'&&c<='9') {
        return c-'0';
    } else if(c>='a'&&c<='z') {
        return c-'a'+10;
    } else if(c>='A'&&c<='Z') {
        return c-'A'+36;
    }
}

void insert() {
    int u=0;
    int len=strlen(st);
    ans+=val[0];
    val[0]++;
    UP(i,0,len) {
        int v;
        for(v=son[u];v!=0;v=nex[v]){
            if(c[v]==st[i]){
                break;
            }
        }
        if(!v) {
            v=sz++;
            c[v]=st[i];
            val[v]=0;
            son[v]=0;
            nex[v]=son[u];
            son[u]=v;
        }
        u=v;
        ans+=val[u]*2;
        val[u]++;
    }
    if(cnt[u]){
        ans+=cnt[u];
    }
    cnt[u]++;
}

int main() {
    int n;
    int ks=1;
    W(~scanf("%d",&n)) {
        if(n==0)
            break;
        MEM(ch,0);
        MEM(val,0);
        MEM(nex,0);
        MEM(son,0);
        MEM(cnt,0);
        MEM(c,0);
        ans=0;
        sz=1;
        UP(i,0,n) {
            scanf("%s",st);
            insert();
        }
        printf("Case %d: %lld\n",ks++,ans);
    }
}
/*
3
abc
abc
abc
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值