TYB电竞队选名字,第一次模拟赛,字典树最长公共前缀

正文

      题目描述:身为电竞队队长的TYB今天要召开电竞队会议啦!由于TYB觉得再电竞队里面,叫原名不太合适,应当取一些电竞队专属名字。比如说,clearlovePDD,狗贼叔叔,UZifaker之类的。TYB经过冥思苦想,想到了n个十分酷炫的名字。电竞队也有恰好n个人。所以人和新的专属名字是一一对应的。现在要把这些专属名字分配给每一个同学,每一个同学分配到一个专属名字,每一个专属名字必须分配给某个同学。。大家都知道,两个名字如果比较像,就会方便记忆。现在定义专属名字和真名之间的相似度是他们之间的最长公共前缀。现在要求分配专属名字,使得匹配的相似度总和最大。


       相信大家都学过字典树,我们直接每次把一个字符串丢进字典树里面,把每个字符串的末尾标记一下(

c[0][x]++   ||   c[1][x]++),再把原名和专属名字区分开(1/0),我们记录一下当前的深度,然后用min(

c[0][当前点],c[1][当前点])*dep(深度)累加答案,我们要的就是让深度越大越好,所以我们在回溯的时候做这个操作即可。如果多出来的怎么办,,比如说,名字有ababb,ababb , 专属名字有ababb,abacc.这时候,在ababb这个位置c[0][ababb]=2,c[1][ababb]=1。多出来的就是剩下的ababb。我们把他往它往上传递,就是将

c[0][abab]++;发现这时还是不行,在往上传递.c[0][aba]++;另一边的c[1][abacc]=1也是多余的,传递上来,

c[1][abac]++;c[1][aba]++;   这时候又可以加一便,答案就是1*5+1*3=5+3=8;

代码<要背板子>

#include
#include
#include
#include
using namespace std;


int n;
int son[800010][30];
int c[2][800010];
int tot;
int t=0;
int ans=0;
char s[800010];


struct Tire{
	void Clear(){tot=1;memset(son[1],0,sizeof(son[1]));memset(c,0,sizeof(c));}
	void Insert(char *s,int x){
		int n=strlen(s);
		int u=1;
		for(int i=0;i			int temp=s[i]-'a';
			if(son[u][temp]==0){
				son[u][temp]=++tot;
				memset(son[tot],0,sizeof(son[tot]));
			}
			u=son[u][temp];
		}
		c[x][u]++;
	}
}xx;


void Find(int x,int fa,int dep){
	for(int i=0;i<26;i++)
		if(son[x][i]!=0) Find(son[x][i],x,dep+1);
	ans+=min(c[0][x],c[1][x])*dep;
	if(c[0][x]>c[1][x]) 
		c[0][fa]+=c[0][x]-c[1][x];
	else if(c[1][x]>c[0][x])
		c[1][fa]+=c[1][x]-c[0][x];
	c[0][x]=c[1][x]=0;
}


int main(){
	scanf("%d",&t);
	while(t--){ 
		scanf("%d",&n);
		xx.Clear();
 		ans=0;
		for(int i=1;i<=n;i++){
			scanf("%s",s);
			xx.Insert(s,0);
		}
		for(int i=1;i<=n;i++){
			scanf("%s",s);
			xx.Insert(s,1);
		}
		Find(1,0,0);
		printf("%d\n",ans);
	}
}

你可能感兴趣的:(TYB电竞队选名字,第一次模拟赛,字典树最长公共前缀)