Time limit 2000 ms
Input
Output
题目大意
(翻译来自lrj蓝书)
strcmp是比较两个字符串字典序大小的函数,本题中实现如上代码
比较操作一直进行到有两个不同字符为止(假定字符串总是以’\0’结尾)
比如strcmp(“than”,“that”)和strcmp(“there”,“the”)都需要比较7次
输入n个字符串,两两调用一次strcmp,问字符总比较次数为多少
题目分析
题目中两个字符串是从头开始逐位比较的
这启发我们使用一个匹配前缀的数据结构,由此很容易想到Trie树
将所有字符串插入Trie树后,显然每个分叉的位置就是一次比较结束的位置
只要边插入边统计即可
然而本题的考点并不在此,而是坑爹的数据范围,普通的Trie树数组根本开不了那么大好吗
所以我们用左儿子右兄弟的链表式储存方式
即每层结点都组成一个链表,其父亲的儿子指针指向它们的表头,如此避免不必要的空间浪费
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const int maxn=4002000;
int n,cnt,cs;
int son[maxn],bro[maxn];
lt sum[maxn],ans;//sum记录子树中的字符串数量
char txt[1010],rem[maxn];
int qnum(char tt)
{
if(tt>='0'&&tt<='9') return tt-'0';
else if(tt>='a'&&tt<='z') return tt-'a'+10;
else if(tt>='A'&&tt<='Z') return tt-'A'+36;
}
void ins(char* ss,int len)
{
int u=0;
for(int i=0;i<=len;i++)//插入时将'\0'也插入方便处理
{
int judge=0,v;
for(v=son[u];v;v=bro[v])
if(rem[v]==ss[i]){ judge=1; break;}
if(!judge)//新建结点插入为表头
{
v=++cnt;
bro[v]=son[u]; rem[v]=ss[i];
son[u]=v; sum[v]=son[v]=0;
}
ans+=(sum[u]-sum[v])*(2*i+1);//一个分叉点统计一次
if(i==len)
{
ans+=sum[v]*(2*i+2);
sum[v]++;
}
sum[u]++; u=v;
}
}
void init()
{
cnt=ans=0;
sum[0]=bro[0]=son[0]=0;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0) break; init();
for(int i=1;i<=n;++i)
{
scanf("%s",&txt);
ins(txt,strlen(txt));
}
printf("Case %d: %lld\n",++cs,ans);
}
return 0;
}