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