51nod1526 分配笔名 trie+贪心

15 篇文章 0 订阅

Description


班里有n个同学。老师为他们选了n个笔名。现在要把这些笔名分配给每一个同学,每一个同学分配到一个笔名,每一个笔名必须分配给某个同学。现在定义笔名和真名之间的相关度是他们之间的最长公共前缀。设笔名为a,真名为b,则他们之间的相关度为lcp(a,b)。那么我们就可以得到匹配的质量是每一个同学笔名和真名之间相关度的和。

现在要求分配笔名,使得匹配质量最大。

Solution


牛逼的题目。。

我们把笔名和真名都插进trie里,按照深度从大到小枚举trie上的节点,如果一个节点的子树内两个size都不为0那么就内部互相匹配,然后暴力跳父亲删掉匹配了的数量

每个串至多被删一次,那么复杂度是等于节点数排序+串长之和的

Code


#include <stdio.h>
#include <string.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fi first
#define se second

typedef long long LL;
typedef std:: pair <int,int> pair;
const int N=800005;

int rc[N][26],size[N][2],dep[N],fa[N],tot=1;

char str[N];

pair p[N];

void ins(char *s,int r) {
	int n=strlen(s+1),x=1;
	rep(i,1,n) {
		size[x][r]++;
		if (!rc[x][s[i]-'a']) {
			rc[x][s[i]-'a']=++tot;
			dep[rc[x][s[i]-'a']]=dep[x]+1;
			fa[tot]=x;
		}
		x=rc[x][s[i]-'a'];
	}
	size[x][r]++;
}

int main(void) {
	freopen("data.in","r",stdin);
	int n; scanf("%d",&n);
	rep(i,1,n) {
		scanf("%s",str+1);
		ins(str,0);
	}
	rep(i,1,n) {
		scanf("%s",str+1);
		ins(str,1);
	}
	rep(i,1,tot) p[i]=pair(-dep[i],i);
	std:: sort(p+1,p+tot+1);
	LL ans=0;
	rep(i,1,tot) {
		int d=std:: min(size[p[i].se][0],size[p[i].se][1]);
		if (!d) continue;
		for (int x=p[i].se;x;x=fa[x]) {
			size[x][0]-=d;
			size[x][1]-=d;
		}
		ans-=1LL*d*p[i].fi;
	}
	printf("%lld\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值