Time limit 2000 ms

UVA - 11732 "strcmp()" Anyone?【Trie】

Input

UVA - 11732 "strcmp()" Anyone?【Trie】

Output

UVA - 11732 "strcmp()" Anyone?【Trie】


题目大意

(翻译来自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;
}

相关文章: