题目
删除元素后 K 个字符串的最长公共前缀
题解
这题字典树的做法我还没学过回头再来补,但是还有另一种做法让人拍案叫绝。我这个思路是来源于B站up主灵茶山艾府,听了佬的讲解让我差点给水杯掉地上咯。代码风格我也没见过,看来有的学了。
关键思路
-
排序优化
- 将字符串按字典序排序后,相同前缀的字符串会聚集在一起。
- 这样,最优的 k 个字符串一定是连续的(因为如果跳过某些字符串,LCP 只会更短)。
-
计算 LCP 的高效方法
- 定理 1:有序数组中,一个连续子数组的 LCP = 该子数组 首尾字符串的 LCP。
- 例如,
["ab", "aba", "abb"]
的 LCP ="ab"
和"abb"
的 LCP ="ab"
(长度为 2)。
- 例如,
- 定理 1:有序数组中,一个连续子数组的 LCP = 该子数组 首尾字符串的 LCP。
-
不删除字符串的情况
- 直接枚举所有长度为
k
的连续子数组,计算它们的 LCP,取最大值mx
。
- 直接枚举所有长度为
-
删除一个字符串的情况
- 定理 2:如果删除的字符串 不在 最优子数组
[mxI, mxI+k-1]
中,答案仍然是mx
(因为删除不影响最优解)。 - 定理 3:如果删除的字符串 在最优子数组内,答案就是 次优解
mx2
(即所有其他长度为k
的子数组的 LCP 的最大值)。- 因为删除后,最优子数组的 LCP 可能变短,但次优解不会比
mx2
更差。
- 因为删除后,最优子数组的 LCP 可能变短,但次优解不会比
- 定理 2:如果删除的字符串 不在 最优子数组
-
实现优化
- 直接对字符串排序会改变原始顺序,因此改为 对下标数组排序,通过比较
words[i]
来间接排序。
- 直接对字符串排序会改变原始顺序,因此改为 对下标数组排序,通过比较
参考代码
class Solution {int calc_lcp(string& s, string& t){int len = min(s.size(), t.size());for(int i = 0; i < len; i ++){if(s[i] != t[i]){return i;}}return len;}
public:vector<int> longestCommonPrefix(vector<string>& words, int k) {int n = words.size();if(k >= n){return vector<int>(n);}//没见过的代码风格,回头二刷,就是原数组不变,拿下标记录他们的排序结果。vector<int> idx(n);ranges::iota(idx, 0);ranges::sort(idx, {}, [&](int i) -> auto& { return words[i]; }); int mx = -1, mx2 = -1, mx_i = -1;for(int i = 0; i <= n - k; i ++){int lcp = calc_lcp(words[idx[i]], words[idx[i + k - 1]]);if(lcp > mx){mx2 = mx;mx = lcp;mx_i = i;}else if(lcp > mx2) mx2 = lcp;}vector<int> ans(n, mx);for (int i = mx_i; i < mx_i + k; i ++) {ans[idx[i]] = mx2; }return ans;}
};