UVA - 11732 Trie 左儿子右兄弟模板

UVA - 11732

这几天到处赠饭领红包,感觉脸都笑得僵硬了。。。在家好没状态,还是早点去学校算了*_*

题意: 输入n个字符串(有大小写字母和数字),两两调用一次strcmp(),一共调用 n*(n+1) 次,问字符比较的总次数是多少?

int strcmp(char *s, char *t)
{
    int i;
    for (i=0; s[i]==t[i]; i++)
        if (s[i]=='\0')
            return 0;
    return s[i] - t[i];
}

注 :s[i]==t[i] 和 s[i]=='\0' 各有一次比校。

tags:明显的字典树,边插入边统计就好了

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define MP make_pair
#define PB push_back
#define fi  first
#define se  second
typedef long long LL;

const int N = 5000005;
struct Trie
{
    char ch[N];
    int sz, son[N], bro[N];
    int tot[N];
    void Init()
    {
        sz=son[0]=bro[0]=tot[0]=0;
    }
    LL  Insert(char* str)
    {
        LL  ret = 0;
        ++tot[0];
        int len=strlen(str), u=0, to;
        for(int i=0; i<=len; ++i)
        {
            for(to=son[u]; to; to=bro[to])
                if(ch[to]==str[i]) break;
            if(to==0)
            {
                to=++sz;  tot[to]=0;
                son[to]=0, ch[to]=str[i];
                bro[to]=son[u], son[u]=to;
            }
            else
            {
                ret += 1LL*tot[to]*2;  ///
            }
            ++tot[to];
            ret += tot[u]-tot[to];   ///
            u = to;
        }
        return ret;
    }
} trie;

char si[1005];
int main()
{
    int cas = 0, n;
    while(~scanf("%d", &n) && n)
    {
        trie.Init();
        LL ans = 0;
        rep(i,1,n) {
            scanf("%s", si);
            ans += trie.Insert(si);
        }
        printf("Case %d: %lld\n", ++cas, ans);
    }

    return 0;
}
/*
3
that
than
thae
2
there
the
*/
View Code

 

一般的字典树题目,我们习惯用双数组解决。但是当字符集很大的时候用双数组的形式很有可能会超内存,这个时候就要用 左儿子右兄弟 的形式储存 trie 树。左儿子右兄弟的形式可以有效地节约内存,而且初始化很快,但是插入和询问是 O(n*log(n)), 算是以时间换取空间。

Trie 左儿子右兄第模板:

// Trie 左儿子右兄弟模板
const int N = 5000005;
struct Trie
{
    char ch[N];// ch[i]为第i个结点上的字符
    int sz, son[N], bro[N];  // 结点总数、左儿子编号、右兄弟编号
    int tot[N];   // tot[i]为第i个结点为根的子树包含的叶子结点总数
    void Init()
    {
        sz=son[0]=bro[0]=tot[0]=0;
    }
    void  Insert(char* str)
    {
        ++tot[0];
        int len=strlen(str), u=0, to;
        for(int i=0; i<len; ++i)
        {
            for(to=son[u]; to; to=bro[to])
                if(ch[to]==str[i]) break;
            if(to==0)  // 没有找到就新建结点
            {
                to=++sz,   tot[to]=0;
                son[to]=0, ch[to]=str[i];
                bro[to]=son[u], son[u]=to;  //插入进去
            }
            ++tot[to];
            u = to;
        }
    }
} trie;

转载于:https://www.cnblogs.com/sbfhy/p/8459823.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值