UVa 11732 strcmp() Anyone?

        题意:这题先展示了一个字符串比较函数strcmp,它的工作原理是这样的,扫描两个字符串的每一位,比较是否相等,如不相等,即可得出结果,如果相等,检查当前位是否为结尾(是结尾表明两个字符串完全相同)。也就是说,两个字符串的比较次数可以这样计算,如果串相等,需比较(长度+1)*2次;否则次数为公共前缀长度*2+1。然后现在有好多字符串,问如果这些字符串两两执行该函数比较,需要比较多少次。

        思路:字典树。随手写了个数组下标代替指针的字典树版本,结果超时了。然后我在此基础上增加了一个链表,把每个节点的所有孩子串起来,还是超时。然后我分析了一下,每个串的长度为1000,有4000个串,也就是最坏的情况下有4000000个节点,每个节点有62个孩子也就是需要4000000*62的空间,不超时也会爆内存(可能因为memset超时)。最后改成了完全以链的形式存储节点的孩子,也就是插入的时候需要沿着链寻找孩子是否已经存在。

        数据结构没问题了,怎么计算比较次数呢?字典树的每一个节点都可以表示一个公共前缀,设具有这样的公共前缀的串的数量为m,那么比较到当前节点时,需要比较的次数就是m*(m-1)/2。递推一下就能算出来了。


#include <iostream>           
#include <stdio.h>           
#include <cmath>           
#include <algorithm>           
#include <iomanip>           
#include <cstdlib>           
#include <string>           
#include <memory.h>           
#include <vector>           
#include <queue>           
#include <stack>           
#include <map>         
#include <set>         
#include <ctype.h>         
#define INF 1<<30       
#define ll long long       
#define max3(a,b,c) max(a,max(b,c))       
#define MAXN 4100000
  
using namespace std;  


struct Trie{
	ll val[MAXN];//具有相同前缀的串的数量 
	ll end[MAXN];//相同串的数量 
	char ch[MAXN];
	int head[MAXN];
	int next[MAXN];
	int sz;
	
	void init(){
		sz=1; val[0]=0;
		memset(head,0,sizeof(head));
		memset(next,0,sizeof(next));
		memset(end,0,sizeof(end));
	}
	
	void insert(char* str){
		int u=0;
		int len=strlen(str);
		val[0]++;
		for(int i=0;i<len;i++){
			
			int t=head[u];
			bool find=false;
			while(t){
				if(ch[t]==str[i]){
					u=t;
					find=1;
					break;
				}
				t=next[t];
			}
			
			if( !find ){
				int v=sz++;
				next[v]=head[u];
				head[u]=v;
				
				val[v]=0;
				ch[v]=str[i];
				head[v]=0;
				u=v;
			}
			val[u]++;
		}
		end[u]++;
	}
	
	ll sear(int u){
		ll re=(val[u]-1)*val[u]/2;
		int t=head[u];
		while(t){
			if(val[t]<=1){
				t=next[t];
				continue;
			}
			re+=(val[t]-1)*val[t]/2;
			re+=(end[t]-1)*end[t]/2;//由于没有存储结束符,需要额外计算一下串相等的情况 
			re+=sear(t);
			
			t=next[t];
		}
		return re;
	}
};

char str[1010];
Trie T;

int main(){
	int n;
	int cas=0;
	while(~scanf("%d",&n)){
		if(!n)break;
		cas++;
		T.init();

		for(int i=1;i<=n;i++){
			scanf("%s",str);
			T.insert(str);
		}
		printf("Case %d: %lld\n",cas,T.sear(0));
	}
}


你可能感兴趣的:(uva,字典树)