UVa 11732 strcmp()函数 trie

QvQ终于搞出来的,条件判断略多

题意是:给出n个字符,用strcmp()来对它们两两这间相互比较,求要比较总次数

n在[1~4000]之间

strcmp()的比较方法很有意思,会对两个字符串对应位置进行比较,如果相同再比较是否为'/0',所以对于“a”和"a"这两上字符串,要比较4次

第一次,比较'a'和'a',发现它们相同,再看‘a'是否等于'\0',不等于,继续比较,到目前为止,比较了两次

第二次,比较’/0'和'/0',发现它们相同,再看是否为'\0',等于,结束比较,直接返回

如果第一次比较就不相同,直接返回


从数据量来看,要用trie树来优化,我的方法是边建树边计算,那么读完所有数据的时间,就能出答案了XD

用u来表示父节点,c为当前的字符,ch[u][c]则用来存其子节点的序号,令cid=ch[u][c];

要开以下几个数组,layer[]用来保存当前字符在树中的第几层,sons[]表示当前节点有几个子节(包括其自己),ends[]表示有多不和个字符串走到当前节点时停止,即相同的字符串数。

(1)当未到达最后一层时

考虑同一层里,与它不相同的字符串

当走到ch[u][c]时,在当前层,与c不相同的字符串一共有sons[u]-sons[cid]个

比如

abcd

abef

当第二串走到第三层时,字符为e, 有一个字符串abcd与它不同,这时,sons[u]为2,sons[cid]=1。。。

比较次数为2*layer[u]+1;

(2)当到达最后一层时

为什么这里要单独说呢=w=因为从1看出,有的计算要用到其父节点,但是最后一层是没有的,额外考虑

答案直接为ans+=(sons[cid]-1)*(2*layer[cid]+1)+ends[cid];

即为其所有子结点(不包括自己)判断次数,再加上到当前节点为止的ends的数目

其实原来应该为ans+=(sons[cid]-1-ends[cid]*(2*layer[cid]+1)+ends[cid]*(2*layer[cid]));

这是因为,对于其它子节点,比如aa和a, 要比较3次,而和a一样在父节点就终结的,要比较4次,

把第二个式整理一下就是第一个了。


以上,就出来了>.<

但是由于UVa不知为何总是submission error,我随便找了个标程,再成了几百组数据,这些测过是没问题的。不知会不会有还没考虑的trick……

(边写边发现原程序的bug…贴出来的和原来,核心部分都改了好多……QAQ,但下面这个过了测试数据了XD,pretest passed)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<string>
#include<iostream>
using namespace std;
const int maxnode=4000*1000+100;
const int sigma_size=10+26*2;
struct Trie
{
	int ch[maxnode][sigma_size];
	int layer[maxnode];
	int ends[maxnode];
	int sons[maxnode];
	int val[maxnode];
	int sz, ans;
	void init()
	{
		sz=1;ans=0;
		memset(val, 0, sizeof(val));
		memset(ends, 0, sizeof(ends));
		memset(ch[0], 0, sizeof(ch[0]));
		memset(layer, 0, sizeof(layer));
		memset(sons, 0, sizeof(sons));
	}
	int idx(char x)
	{
		if(isdigit(x))return x-'0';
		else if(islower(x))return x-'a'+10;
		else if(isupper(x))return x-'A'+36;
	}
	void insert(string &s, int v)
	{
		sons[0]++;
		int u=0, n=s.size();
		for(int i=0;i<n;i++)
		{
			int c=idx(s[i]);
			if(!ch[u][c])
			{
				memset(ch[sz], 0, sizeof(ch[sz]));
				val[sz]=0;
				ch[u][c]=sz++;
			}
			int cid=ch[u][c];
			layer[cid]=layer[u]+1;
			sons[cid]++;
			ans+=(sons[u]-sons[cid])*(2*layer[u]+1);		
			if(i==n-1)ans+=(sons[cid]-1)*(2*layer[cid]+1)+ends[cid];
			u=cid;
		}
		val[u]=v;
		ends[u]++;
	}
}trie;
int main()
{
	int n;
	int cnt=0;
	string str;
	while(~scanf("%d", &n), n)
	{
		cnt++;
		trie.init();
		for(int i=0;i<n;i++)
		{
			str.clear();
			cin>>str;			
			trie.insert(str, 1);
			//cout<<trie.ans<<endl;
		}
		printf("Case %d: %d\n", cnt, trie.ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值