题意:这题先展示了一个字符串比较函数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)); } }