https://codingcompetitions.withgoogle.com/kickstart/round/000000000019ffc7/00000000001d3ff3
题目大意:每组数据给
n
n
n个字符串,每个字符串仅能划分到某一个组里面,且这个组的价值就等于该组所有字符串的最长公共前缀的长度。现在给一个数
k
k
k,让你给这些字符串划分组,使得所有刚好含有
k
k
k个字符串的组的价值之和最大。
思路:把这些字符串全部插到字典树里面,同时记录每个前缀的数量和长度,对于含有 k k k个字符串的一个组,因为它的价值和它们的最长公共前缀的长度有关系,贪心的想肯定要取最长的那个前缀。那么我们对字典树进行 d f s dfs dfs,设当前节点为 u u u,对它的所有子节点进行 d f s dfs dfs后,假设得到了一个值 s u m sum sum,表示 u u u的子节点中已经配对了 s u m sum sum个字符串,那么结合字典树的性质不难想到对于 u u u节点来说,可用的前缀只剩下 c n t [ u ] − s u m cnt[u]-sum cnt[u]−sum个了,我们用这个数判断和 k k k的关系,更新答案即可。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=2e6+5;
int tot,ans,n,k;
char s[maxn];
int tree[maxn][26],cnt[maxn],len[maxn];
void Insert()
{
int le=strlen(s);
int rt=0;
for(int i=0;i<le;i++)
{
if(!tree[rt][s[i]-'A'])
tree[rt][s[i]-'A']=++tot;
rt=tree[rt][s[i]-'A'];
++cnt[rt];
len[rt]=i+1;
}
}
int dfs(int u,int sum)
{
for(int i=0;i<26;i++)
{
if(tree[u][i])
{
sum+=dfs(tree[u][i],0);
tree[u][i]=0;
}
}
int tmp=(cnt[u]-sum)/k;
if(tmp)
{
ans+=tmp*len[u];
sum+=tmp*k;
}
cnt[u]=len[u]=0;
return sum;
}
int main()
{
int t,times=0;
scanf("%d",&t);
while(t--)
{
tot=ans=0;
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
{
scanf("%s",s);
Insert();
}
dfs(0,0);
printf("Case #%d: %d\n",++times,ans);
}
return 0;
}