2024年蓝桥杯省赛B组前缀总分(洛谷P12124):前缀总分详解

一、问题描述
给定N个字符串,定义字符串i和j的前缀相似度为它们的最长公共前缀长度LCP(i,j)。要求计算所有字符串对(i<j)的LCP之和,并允许修改任意一个字符串的任意一个字符(改为小写字母),求可能获得的最大总分值。
二、完整代码解析(含详细注释)
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
// 预处理所有字符串对的LCP(最长公共前缀)
vector<vector<int>> precompute_lcp(const vector<string>& strs) {
int n = strs.size();
vector<vector<int>> lcp(n, vector<int>(n, 0)); // n×n的LCP矩阵
for (int i = 0; i < n; ++i) {
for (int j = i+1; j < n; ++j) {
int len = 0;
// 逐字符比较直到不匹配或到达任一字符串末尾
while (len < strs[i].size() && len < strs[j].size()
&& strs[i][len] == strs[j][len]) {
len++;
}
lcp[i][j] = lcp[j][i] = len; // LCP是对称的
}
}
return lcp;
}
// 计算当前LCP矩阵的总分
long long compute_total(const vector<vector<int>>& lcp) {
long long total = 0;
int n = lcp.size();
// 只计算i<j的对,避免重复计算
for (int i = 0; i < n; ++i) {
for (int j = i+1; j < n; ++j) {
total += lcp[i][j];
}
}
return total;
}
// 主求解函数
long long solve(vector<string>& strs) {
int n = strs.size();
auto lcp = precompute_lcp(strs); // 预处理LCP
long long original = compute_total(lcp); // 原始总分
long long max_score = original; // 初始化最大分数
// 尝试修改每个字符串的每个位置
for (int i = 0; i < n; ++i) {
string original_str = strs[i];
vector<int> original_contrib(n, 0); // 记录原始贡献值
// 预先计算当前字符串对其他所有字符串的原始贡献
for (int j = 0; j < n; ++j) {
if (j != i) original_contrib[j] = lcp[i][j];
}
// 遍历字符串的每个位置
for (int pos = 0; pos < original_str.size(); ++pos) {
char original_char = original_str[pos];
// 尝试修改为其他25个小写字母
for (char c = 'a'; c <= 'z'; ++c) {
if (c == original_char) continue; // 跳过不修改的情况
long long delta = 0; // 记录分数变化量
// 计算对每个其他字符串的影响
for (int j = 0; j < n; ++j) {
if (j == i) continue; // 不计算自己与自己的LCP
// 新LCP长度不超过原始值和当前位置
int new_len = min(original_contrib[j], pos);
// 只有当原始LCP>=pos时才需要重新计算
if (new_len == pos) {
// 重新计算从pos开始的新LCP
while (new_len < strs[i].size() && new_len < strs[j].size()) {
char ci = (new_len == pos) ? c : strs[i][new_len];
if (ci != strs[j][new_len]) break;
new_len++;
}
}
delta += (new_len - original_contrib[j]); // 累计变化量
}
// 更新最大分数
max_score = max(max_score, original + delta);
}
}
}
return max_score;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n; cin >> n;
vector<string> strs(n);
for (int i = 0; i < n; ++i) cin >> strs[i];
cout << solve(strs) << endl;
return 0;
}三、算法核心思想
四、常见问题解答
Q:为什么需要预处理LCP? A:预处理可以避免重复计算,是典型的空间换时间策略
Q:如何确定修改哪个字符最优? A:通过枚举所有可能的字符修改,保证不会遗漏最优解
Q:算法能否处理大规模数据? A:当前解法适合N≤100的情况,更大规模需要进一步优化
原创内容 转载请注明出处
