UVa:11732 strcmp() Anyone?

折腾了两个多小时终于把这个题给过了。

一开始想到思路直接写了个多叉的tire,结果超时了。感觉是不能再小的算法了,后来发现只memset就要很长时间,于是改成每次开辟新节点的时候再memset,还加了读入输出外挂,结果还是超时。后来上网题解上说要用左儿子右兄弟表示法来做,然后写了一个,1.7sAC了。

多叉的写法,由于它直接利用下标表示对应的字符,所以对空间浪费比较严重,但是要找到某个结点的孩子结点是O(1)的。

而二叉的写法,是通过每次比较查找来找到对应字符的,所以没有浪费的空间,但是比较费时。

多叉和二叉的写法,就好像是邻接矩阵和邻接表似的。

这个题多叉写法会超时,大概是数组开的过大,每次memset超时了(想起之前写的几个tire的代码,也都很慢,可能是这个原因)。

用左儿子右兄弟的写法,原来每个case都把全部数组memset一遍,结果跑了1.8s。后来改掉了这块,改成每次建树的时候清空每个结点,变成了0.8s。

这个题的思路,每次建树的时候就考虑每个结点字符出现的次数,与当前结点之前出现次数的比较,这里比较两次,跟父亲结点的比较,这里要去掉父亲结点中当前结点的那块,只比较一次。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#define ll long long
#define INF 200000000
#define MOD 20071027
#define MAXN 4000*1005
using namespace std;
ll ans;
struct Tire
{
    int child[MAXN],next[MAXN],val[MAXN];
    char str[MAXN];
    int sz;
    void Init()
    {
        sz=1;
        child[0]=0;
        next[0]=0;
        val[0]=0;
    }
    void Insert(char *word)
    {
        int u=0,v;
        val[u]++;
        for(int i=0; word[i]; ++i)
        {
            bool ok=false;
            for(v=child[u]; v; v=next[v])
            {
                if(str[v]==word[i])
                {
                    ok=true;
                    break;
                }
            }
            if(!ok)
            {
                str[sz]=word[i];
                val[sz]=0;
                child[sz]=0;
                v=sz++;
                next[v]=child[u];
                child[u]=v;
            }
            ans+=val[v]*2;
            val[v]++;
            ans+=val[u]-val[v];
            u=v;
        }
    }
};
Tire tree;
char word[1005];
int main()
{
    int n,kase=0;
    while(scanf("%d",&n)!=EOF&&n)
    {
        getchar();
        ans=0;
        tree.Init();
        for(int i=0; i<n; ++i)
        {
            int j=0;
            while(word[j]=getchar())
            {
                if(word[j]=='\n') break;
                j++;
            }
            word[j]='#';
            word[j+1]=0;
            tree.Insert(word);
        }
        printf("Case %d: %lld\n",++kase,ans);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值