1. 题意
题目核心要求:
给定一个字符串集合,需要从中选取任意非空子集合,计算该子集合中所有字符串的最长公共前缀长度 与子集合元素个数 的乘积,最终输出所有可能子集合对应的乘积的最大值。
2. 思路
这是一道典型的字典树应用问题,核心思路与前缀统计类问题一致,字典树的每个节点存储以该节点对应的前缀为公共前缀的字符串数量 (下称「前缀计数」)。
设所有字符串的集合为 $U$,任意非空子集合 $S \subset U$,记:
- $lcp(S)$:集合 $S$ 中所有字符串的最长公共前缀长度;
- $|S|$:集合 $S$ 的元素个数。
题目要求最大化 $lcp(S) \times |S|$,即求 $\max\limits_{S \subset U, S \neq \emptyset}{lcp(S) \times |S|}$。
解决方法:
- 构建字典树 :将所有字符串插入字典树,每个节点记录经过该节点的字符串数量(即前缀计数);
- 遍历查询最大值 :对每个字符串,在字典树中逐字符遍历其前缀:
- 遍历到第 $k$ 个字符时,当前节点的前缀计数为「以该 $k$ 长度前缀为公共前缀的字符串总数」;
- 计算该前缀对应的乘积($k \times$ 前缀计数),并更新全局最大值;
注意空间 :直接开二维数组存储字典树节点会爆,要爆开 unordered_map (要自定义 pair 类型的哈希函数)。
3. 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include<bits/stdc++.h> using namespace std; int n,cnt=1,sum[20000010]; string c[1000010]; struct hashfunc{ template<typename T,typename U> size_t operator()(const pair<T,U> &i)const{ return hash<T>()(i.first)^hash<U>()(i.second); } }; unordered_map<pair<int,int>,int,hashfunc>son; void insert(string s){ int now=1; for(auto ch:s){ if(ch>='a')ch-='a'; else if(ch!=' ') ch=ch-'A'+26; else ch=52; if(!son[{now,ch}])son[{now,ch}]=++cnt; now=son[{now,ch}]; sum[now]++; } } int query(string s){ int now=1,ans=0,siz=0; for(auto ch:s){ if(ch>='a')ch-='a'; else if(ch!=' ') ch=ch-'A'+26; else ch=52; now=son[{now,ch}]; siz++; ans=max(ans,sum[now]*siz); } return ans; } main(){ cin.tie(0)->ios::sync_with_stdio(0); cin>>n; for(int i=1;i<=n;i++){ cin>>c[i]; insert(c[i]); } int ans=0; for(int i=1;i<=n;i++)ans=max(ans,query(c[i])); cout<<ans; return 0; }
|