UVa11732 "strcmp()" Anyone?(Trie树+孩子兄弟表示法)

我的做法是先建字典树,统计每个结点出现次数和相同字符串个数,每个结点对答案的贡献就是2*C(次数,2),然后再分别讨论相同字符串和不同字符串对答案的贡献。

另外这题主要就是Trie树的孩子兄弟表示法:

因为数据范围,4000个字符串每个字符串长度1000且字符的范围有62个,用孩子链表表示法最坏,4000*1000*62,再*4Byte,近1GB的内存,太大了,有许多空指针浪费空间。

所以需要用孩子兄弟表示法压缩字典树,4000*1000*2,左孩子右兄弟,用时间换空间。

 1 #include<cstdio>
 2 #include<cstring>
 3 using namespace std;
 4 char key[4100000];
 5 int ch[4100000][2],cnt[4100000],flag[4100000],tn;
 6 void insert(char *s){
 7     int x=0;
 8     for(int i=0; s[i]; ++i){
 9         if(ch[x][0]==0){
10             ch[x][0]=++tn;
11             x=ch[x][0];
12             key[x]=s[i];
13             ++cnt[x];
14             continue;
15         }
16         for(x=ch[x][0]; key[x]!=s[i] && ch[x][1]; x=ch[x][1]);
17         if(key[x]!=s[i]){
18             ch[x][1]=++tn;
19             x=ch[x][1];
20             key[x]=s[i];
21             ++cnt[x];
22         }else ++cnt[x];
23     }
24     ++flag[x];
25 }
26 
27 int main(){
28     int t=0,n;
29     char str[1111];
30     while(~scanf("%d",&n) && n){
31         tn=0;
32         memset(key,0,sizeof(key));
33         memset(ch,0,sizeof(ch));
34         memset(cnt,0,sizeof(cnt));
35         memset(flag,0,sizeof(flag));
36         while(n--){
37             scanf("%s",str);
38             insert(str);
39         }
40         long long res=0;
41         int tot=0,sn=0;
42         for(int i=1; i<=tn; ++i){
43             res+=(cnt[i]-1)*cnt[i];
44             if(flag[i]){
45                 res+=(flag[i]-1)*flag[i];
46                 tot+=flag[i];
47             }
48         }
49         long long tmp=0;
50         for(int i=1; i<=tn; ++i){
51             if(flag[i]) tmp+=(tot-flag[i])*flag[i];
52         }
53         res+=tmp>>1;
54         printf("Case %d: %lld
",++t,res);
55     }
56     return 0;
57 }
原文地址:https://www.cnblogs.com/WABoss/p/5162819.html