#P3293. 第2题-版本选择
-
1000ms
Tried: 910
Accepted: 147
Difficulty: 5
所属公司 :
华为
时间 :2025年6月18日-暑期实习
-
算法标签>模拟
第2题-版本选择
题解
题面描述
在云化平台中,组件按版本号统一管理,版本号遵循语义化版本控制,格式为:
major.minor.patch
- major:重大更新,可能包含向后不兼容的功能。
- minor:在向后兼容的前提下新增功能。
- patch:在向后兼容的前提下修复错误,没有新增功能。
业务App在配置文件中声明其依赖组件及版本,支持如下三种声明方式:
*:取仓库中最新版本。^major[.minor[.patch]]:匹配相同major且版本号不低于声明版本的最大版本。-major.minor[.patch]或~major.minor[.patch]:匹配相同 major 和 minor 且版本号不低于声明版本的最大版本(即只更新补丁)。
说明:
-与~前缀等价,示例中使用-时也表示只更新补丁。
版本号缩写
声明时可省略 minor 或 patch,未写出部分默认补齐为0,例如:
2等价于2.0.01.2等价于1.2.0
解题思路
-
解析版本号声明:
- 若声明为
*,直接从所有版本中选最大。 - 若声明以
^开头,解析后缀版本为三元组(major_0,minor_0,patch_0),保留所有满足major = major_0且版本三元组 >= (major_0,minor_0,patch_0)的版本。 - 若声明以
-或~开头,解析后缀版本为三元组(major_0,minor_0,patch_0),保留满足major = major_0且minor = minor_0且版本三元组 >= (major_0,minor_0,patch_0)的版本。
- 若声明为
-
版本号标准化:
- 按
.分割字符串,补齐至三元组(major,minor,patch)。
- 按
-
比较规则:
- 按字典序比较三元组:先比major,再比minor,最后比patch。
-
遍历筛选:
- 遍历所有三元组,按声明方式过滤后,记录最大者。
-
格式化输出:
-
若无有效版本输出
None。 -
否则省略尾部
.0:- 若
patch = 0,只输出major.minor; - 若
minor = 0且patch = 0,只输出major; - 否则输出完整
major.minor.patch。
- 若
-
C++
#include <bits/stdc++.h>
using namespace std;
// 解析版本号,并补齐为三元组
array<int,3> parse(const string &s) {
array<int,3> v = {0,0,0};
stringstream ss(s);
string part;
int i = 0;
while (getline(ss, part, '.') && i < 3) {
v[i++] = stoi(part);
}
return v;
}
// 三元组比较,大于返回 true
bool greaterThan(const array<int,3>& a, const array<int,3>& b) {
return a > b;
}
// 三元组是否 >=
bool atLeast(const array<int,3>& v, const array<int,3>& req) {
return v >= req;
}
string formatVersion(const array<int,3>& v) {
if (v[2] == 0) {
if (v[1] == 0) return to_string(v[0]);
return to_string(v[0]) + "." + to_string(v[1]);
}
return to_string(v[0]) + "." + to_string(v[1]) + "." + to_string(v[2]);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int N;
cin >> N;
vector<array<int,3>> versions(N);
string s;
for (int i = 0; i < N; i++) {
cin >> s;
versions[i] = parse(s);
}
cin >> s;
array<int,3> best = {-1,-1,-1};
if (s == "*") {
for (auto &v : versions) if (greaterThan(v, best)) best = v;
} else {
char mode = s[0];
auto req = parse(s.substr(1));
for (auto &v : versions) {
if (mode == '^') {
if (v[0] != req[0] || !atLeast(v, req)) continue;
} else if (mode == '-' || mode == '~') {
if (v[0] != req[0] || v[1] != req[1] || !atLeast(v, req)) continue;
}
if (greaterThan(v, best)) best = v;
}
}
if (best[0] < 0) cout << "None";
else cout << formatVersion(best);
return 0;
}
Python
import sys
def parse(s):
parts = s.split('.')
return tuple(int(parts[i]) if i < len(parts) else 0 for i in range(3))
def format_version(v):
major, minor, patch = v
if patch == 0:
if minor == 0:
return f"{major}"
return f"{major}.{minor}"
return f"{major}.{minor}.{patch}"
N = int(sys.stdin.readline())
versions = [parse(sys.stdin.readline().strip()) for _ in range(N)]
req = sys.stdin.readline().strip()
best = (-1, -1, -1)
def at_least(v, target):
return v >= target
if req == '*':
best = max(versions)
else:
mode = req[0]
target = parse(req[1:])
for v in versions:
if mode == '^':
if v[0] != target[0] or not at_least(v, target):
continue
if mode in ('-', '~'):
if v[0] != target[0] or v[1] != target[1] or not at_least(v, target):
continue
if v > best:
best = v
if best[0] < 0:
print('None')
else:
print(format_version(best))
Java
import java.io.*;
import java.util.*;
public class Main {
private static int[] parse(String s) {
String[] parts = s.split("\\.");
int[] v = {0,0,0};
for (int i = 0; i < parts.length && i < 3; i++) {
v[i] = Integer.parseInt(parts[i]);
}
return v;
}
private static String formatVersion(int[] v) {
if (v[2] == 0) {
if (v[1] == 0) return String.valueOf(v[0]);
return v[0] + "." + v[1];
}
return v[0] + "." + v[1] + "." + v[2];
}
private static int compare(int[] a, int[] b) {
for (int i = 0; i < 3; i++) if (a[i] != b[i]) return a[i] - b[i];
return 0;
}
private static boolean atLeast(int[] v, int[] req) {
for (int i = 0; i < 3; i++) {
if (v[i] < req[i]) return false;
if (v[i] > req[i]) return true;
}
return true;
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
List<int[]> versions = new ArrayList<>();
for (int i = 0; i < N; i++) {
versions.add(parse(br.readLine()));
}
String req = br.readLine();
int[] best = {-1,-1,-1};
if (req.equals("*")) {
for (int[] v : versions) if (compare(v, best) > 0) best = v;
} else {
char mode = req.charAt(0);
int[] target = parse(req.substring(1));
for (int[] v : versions) {
if (mode == '^') {
if (v[0] != target[0] || !atLeast(v, target)) continue;
} else if (mode == '-' || mode == '~') {
if (v[0] != target[0] || v[1] != target[1] || !atLeast(v, target)) continue;
}
if (compare(v, best) > 0) best = v;
}
}
if (best[0] < 0) System.out.println("None");
else System.out.println(formatVersion(best));
}
}
题目内容
云化平台对于组件按版本号进行统一管理,版本号遵循语义版本控制系统,格式为:
major.minor.patch
其中
major: 项目进行重大更新,且包含不向后兼容的功能
minor:仅在以向后兼容的方式添加新功能
patch:仅在以向后兼容的方式修复错误,并且没有添加任何新的功能
业务App在其配置文件中声明其依赖的组件及版本,如:1.1.0
并且版本号可以缩写,没有写出来的部分表示为0,如:

对于其依赖的版本号,有三种声明方式:
1.指定最新版本
业务App的配置方式为: *
它最终会匹配仓库中的最新版本给业务App使用。
1.取major版本一致情况下的最大版本
由于major版本之间可能不兼容,指定主版本号,业务App的配置方式为:^1.2.0
这样写会尝试匹配主版本号为1的最大版本。
更多样例含义:

1.保证major和minor版本一致,只取最新的patch版本,写法为:
只进行Bug修复,只更新patch,业务App的配置方式为:~1.2.0 或者 -1.2.0
这样写会取major版本号为1、minor版本2、patch版本号最大的版本。
输入描述
第一行给出当前仓库中指定组件的版本数量N,后续N行给出具体的每一个版本号(注意这里不保证版本号的大小顺序),最后一行给出业务App组件的版本号声明。
3<=N<=100000
版本号中的major、minor和patch都满足0<=yersion<=100000
输出描述
输出最终匹配到的版本号。
如果没有配置的版本号则输出None
样例1
输入
5
1.1.1
2.1
1.2.2
1.2.6
1.3.3
^1.2
输出
1.3.3
说明
对于目标组件,当前在仓库中有5个版本,分别为 1.1.1 2.1 1.2.2 1.2.6 1.3.3
业务App声明的组件依赖为^1.2,系统最终为其匹配到的版本为1.3.3
样例2
输入
5
1.1.1
2.1
1.2.2
1.2.6
1.3.3
*
输出
2.1
说明
*表示取所有版本中的最高版本,即2.1
样例3
输入
5
1.1.1
2.1
1.2.2
1.2.6
1.3.3
-1.2
输出
1.2.6
说明
−表示在major.minor不变的情况下取patch最大的版本号,这里
即:1.2.6
样例4
输入
6
1.1.1
2.1
1.2.2
1.2.6
1.3.3
1.4
^1.6
输出
None
说明
major版本号为1 minor大于等于6的版本号不存在,输出None