Uva 11732 "strcmp()" Anyone? (字典树)

两个字符串匹配时,如果对应位置相同,则需要比较两次(第二次判断是否为'\0') ,若失败只需比较一次。

因此,把所有字符串建成一个字典树,每个节点用num记录有多少个字符串经过这里。

那么对于这个位置,考虑匹配成功的情况,共发生num×(num-1)/2×2次比较。

对于这个节点的每一个后继结点,设该节点的num值为tnum,那么在前驱节点上有num-tnum个和该节点字符不相同的字符,比较的次数为(num-tnum)×tnum。对每个后继节点都求出这个值,做和,因为两两间比较都被算了两次,再除二,就是匹配不成功的比较次数。

深度遍历这颗树,对每个节点都求这两个值做和即可。


注意:由于大多数节点上可能只有一两个后继节点,不能写成节点中开一个数组ch[26]这种写法,会T掉。要写成邻接表的形式。


代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define LL long long
int idx(char c){
	return c-'a';
}

struct node{
	LL num;
	char ch;
	node(){
		num=0;
	};
};
vector <int> G[3000000];
node N[3000000];
int Cn;

void init(){
	int t=0;
	for(int i=0;i<Cn;i++){
		G[i].clear();
		N[i].num=0;
	}
	Cn=1;
}
void input(char s[]){
	int cur=0;
	int len=strlen(s);
	N[cur].num++;
	for(int i=0;i<=len;i++){
		int next=0;
		for(int j=0;j<G[cur].size();j++){
			int tmp=G[cur][j];
			if(N[tmp].ch==s[i]){
				next=tmp;
				break;
			}
		}
		if(!next){
			N[Cn].ch=s[i];
			G[cur].push_back(Cn);
			next=Cn;
			Cn++;
		}
		N[next].num++;
		cur=next;
	}
}

LL solve(int n){
	LL res=0;
	LL tmp=0;
	if(n) res+=N[n].num*(N[n].num-1);
	for(int i=0;i<G[n].size();i++){
		int t=G[n][i];
		res+=solve(t);
		tmp+=(N[n].num-N[t].num)*N[t].num;
	}
	res+=tmp/2;
	return res;
}

int main(){
	int T;
	int kase=1;
	while(~scanf("%d",&T)){
		if(!T) break;
		init();
		for(int i=0;i<T;i++){
			char s[1005];
			scanf("%s",s);
			input(s);
		}
		printf("Case %d: %lld\n",kase++,solve(0));
	}
	return 0;
}



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