UVA11732 左儿子右兄弟 Trie

4 篇文章 0 订阅

tot[i]记录每个以i为根的树的叶子节点的数目

注意插入的时候要插入‘\0’   为了分辨 前缀和相同的串

对于叶子节点  ans+=tot[u]*(tot[u]-1)/2*2*depth;

即相同的串两两一对的对数 *(2*深度)

对于非叶子节点 统计以其为根节点的串 两两一对的对数

           for(int v=head[u];v;v=next[v]){
                sum+=tot[v]*(tot[u]-tot[v]);//该节点和其他兄弟节点的叶子节点数(串数)
            }
            ans+=sum/2*(2*depth+1);///重复计算 除2

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=4010*1000+10;
typedef long long ll;
struct Trie
{
    int head[maxn];
    int next[maxn];
    int tot[maxn];
    char ch[maxn];
    int sz;
    ll ans;
    void clear() {
        head[0]=next[0]=tot[0]=0;
        ans=0;
        sz=1;
    }
    void insert(char *s) {
        int l=strlen(s),v,u=0;
        tot[0]++;
        for(int i = 0; i <= l; ++i) {
            bool found=0;
            for(v=head[u];v!=0;v=next[v]) {
                if(ch[v]==s[i]) {
                    found=1;
                    break;
                }
            }
            if(!found) {
                v=sz++;
                ch[v]=s[i];
                next[v]=head[u];
                head[u]=v;
                head[v]=0;
                tot[v]=0;
            }
            u=v;
            tot[u]++;
        }
    }
    void dfs(int depth,int u) {
        if(head[u]==0) {
            ans+=tot[u]*(tot[u]-1)/2*2*depth;
        }
        else {
            ll sum=0;
            for(int v=head[u];v;v=next[v]){
                sum+=tot[v]*(tot[u]-tot[v]);
            }
            ans+=sum/2*(2*depth+1);
            for(int v=head[u];v;v=next[v]) {
                dfs(depth+1,v);
            }
        }
    }
    ll count()
    {
        dfs(0,0);
        return ans;
    }
};
Trie trie;
int n;
char c[4010];
int main()
{
    int Kase=0;
    while(~scanf("%d",&n)&&n) {
        trie.clear();
        for(int i = 0; i < n; ++i) {
            scanf("%s",c);
            trie.insert(c);
        }
        printf("Case %d: %lld\n",++Kase,trie.count());
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值