#P3555. 第2题-多段数据下发
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 697
            Accepted: 187
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2025年9月3日-非AI方向(通软&嵌软&测试&算法&数据科学)
                              
                      
          
 
- 
                        算法标签>模拟          
 
第2题-多段数据下发
题目描述
给定一系列“配置”与“删除”操作,每个操作都包含一个或多个数据段(范围)。
- 配置操作:将新下发的范围合并(并集)到已有数据库中,不覆盖已有配置。
 - 删除操作:从已有数据库中减去(差集)下发的范围。
 
最终要求将数据库中的所有区间:
- 不可再合并;
 - 按从小到大排序;
 - 单个元素的区间以单个数字形式保存。
 
例如,初始为空,依次下发:
algorithm 1-9,10,17-20,15-15undo algorithm 5-11,16
最终结果为 $1-4,15,17-20$。
思路
- 
数据结构:维护一个
vector<pair<int,int>> intervals,表示当前已存数据库区间集合。 - 
解析输入:将每个操作行拆分,识别是否为删除(前缀
undo),再将“原子数据”如a或a-b转成(a,a)或(a,b)。 - 
合并操作
- 将待添加的所有区间加入到
intervals中; - 按左端点升序排序;
 - 依次扫描,将能合并或相邻(若
cur.second+1 >= next.first)的区间合并成一个。 
 - 将待添加的所有区间加入到
 - 
删除操作
- 
对于每个现存区间
[l,r],遍历所有待删区间[dl,dr],在两个区间有重叠时,将[l,r]切分为:(l, dl-1)(若l < dl)(dr+1, r)(若dr < r)
 - 
收集所有切分后的区间,再做一次合并与排序以保证结构正确。
 
 - 
 - 
输出格式化
- 对最终
intervals,若l==r输出l,否则输出l-r,区间间用逗号隔开; - 若空集,输出
0。 
 - 对最终
 
C++
#include <bits/stdc++.h>
using namespace std;
// 将字符串 data 拆分为区间列表
vector<pair<int,int>> parse(const string &data) {
    vector<pair<int,int>> res;
    stringstream ss(data);
    string token;
    while (getline(ss, token, ',')) {
        int dash = token.find('-');
        if (dash == string::npos) {
            int x = stoi(token);
            res.emplace_back(x, x);
        } else {
            int a = stoi(token.substr(0, dash));
            int b = stoi(token.substr(dash+1));
            res.emplace_back(a, b);
        }
    }
    return res;
}
// 对区间列表进行合并(并集)
vector<pair<int,int>> mergeIntervals(vector<pair<int,int>> iv) {
    if (iv.empty()) return {};
    sort(iv.begin(), iv.end());
    vector<pair<int,int>> ans;
    auto [l, r] = iv[0];
    for (int i = 1; i < iv.size(); i++) {
        auto [l2, r2] = iv[i];
        if (r + 1 < l2) {
            ans.emplace_back(l, r);
            l = l2; r = r2;
        } else {
            r = max(r, r2);
        }
    }
    ans.emplace_back(l, r);
    return ans;
}
// 删除操作:从 iv 中减去 del 区间集合
vector<pair<int,int>> subtractIntervals(const vector<pair<int,int>> &iv,
                                        const vector<pair<int,int>> &del) {
    vector<pair<int,int>> cur = iv;
    for (auto [dl, dr] : del) {
        vector<pair<int,int>> tmp;
        for (auto [l, r] : cur) {
            if (r < dl || dr < l) {
                // 无重合,保留
                tmp.emplace_back(l, r);
            } else {
                // 有重合,切分
                if (l < dl) tmp.emplace_back(l, dl - 1);
                if (dr < r) tmp.emplace_back(dr + 1, r);
            }
        }
        cur = move(tmp);
    }
    // 删除后可能产生可合并区间,统一合并
    return mergeIntervals(cur);
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<pair<int,int>> db;  // 当前数据库区间
    string line;
    getline(cin, line);  // 读掉行尾
    while (n--) {
        getline(cin, line);
        bool isUndo = false;
        string data;
        if (line.rfind("undo", 0) == 0) {
            // 删除操作
            isUndo = true;
            data = line.substr(line.find(' ', 5) + 1);
        } else {
            // 配置操作
            data = line.substr(line.find(' ') + 1);
        }
        auto segs = parse(data);
        if (isUndo) {
            if (!db.empty())
                db = subtractIntervals(db, segs);
        } else {
            // 合并新增区间到 db
            db.insert(db.end(), segs.begin(), segs.end());
            db = mergeIntervals(db);
        }
    }
    // 输出结果
    if (db.empty()) {
        cout << 0;
    } else {
        for (int i = 0; i < db.size(); i++) {
            auto [l, r] = db[i];
            if (l == r) cout << l;
            else cout << l << "-" << r;
            if (i + 1 < db.size()) cout << ",";
        }
    }
    return 0;
}
Python
import sys
def parse(data):
    segs = []
    for token in data.split(','):
        if '-' in token:
            a, b = map(int, token.split('-'))
        else:
            a = b = int(token)
        segs.append((a, b))
    return segs
def merge_intervals(iv):
    if not iv: return []
    iv.sort()
    res = []
    l, r = iv[0]
    for nl, nr in iv[1:]:
        if r + 1 < nl:
            res.append((l, r))
            l, r = nl, nr
        else:
            r = max(r, nr)
    res.append((l, r))
    return res
def subtract_intervals(iv, dels):
    cur = iv
    for dl, dr in dels:
        tmp = []
        for l, r in cur:
            if r < dl or dr < l:
                tmp.append((l, r))
            else:
                if l < dl:
                    tmp.append((l, dl-1))
                if dr < r:
                    tmp.append((dr+1, r))
        cur = tmp
    return merge_intervals(cur)
def main():
    n = int(sys.stdin.readline())
    db = []
    for _ in range(n):
        line = sys.stdin.readline().strip()
        if line.startswith("undo"):
            data = line.split(maxsplit=2)[2]
            segs = parse(data)
            if db:
                db = subtract_intervals(db, segs)
        else:
            data = line.split(maxsplit=1)[1]
            segs = parse(data)
            db.extend(segs)
            db = merge_intervals(db)
    if not db:
        print(0)
    else:
        print(','.join(f"{l}-{r}" if l!=r else str(l) for l, r in db))
if __name__ == "__main__":
    main()
Java
import java.util.*;
public class Main {
    // 解析“a”或“a-b”格式的字符串
    static List<int[]> parse(String data) {
        List<int[]> res = new ArrayList<>();
        for (String token : data.split(",")) {
            if (token.contains("-")) {
                String[] parts = token.split("-");
                res.add(new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1])});
            } else {
                int x = Integer.parseInt(token);
                res.add(new int[]{x, x});
            }
        }
        return res;
    }
    // 合并区间
    static List<int[]> mergeIntervals(List<int[]> iv) {
        if (iv.isEmpty()) return new ArrayList<>();
        iv.sort(Comparator.comparingInt(a -> a[0]));
        List<int[]> ans = new ArrayList<>();
        int l = iv.get(0)[0], r = iv.get(0)[1];
        for (int i = 1; i < iv.size(); i++) {
            int nl = iv.get(i)[0], nr = iv.get(i)[1];
            if (r + 1 < nl) {
                ans.add(new int[]{l, r});
                l = nl; r = nr;
            } else {
                r = Math.max(r, nr);
            }
        }
        ans.add(new int[]{l, r});
        return ans;
    }
    // 删除区间
    static List<int[]> subtractIntervals(List<int[]> iv, List<int[]> dels) {
        List<int[]> cur = iv;
        for (int[] del : dels) {
            int dl = del[0], dr = del[1];
            List<int[]> tmp = new ArrayList<>();
            for (int[] seg : cur) {
                int l = seg[0], r = seg[1];
                if (r < dl || dr < l) {
                    tmp.add(new int[]{l, r});
                } else {
                    if (l < dl) tmp.add(new int[]{l, dl - 1});
                    if (dr < r) tmp.add(new int[]{dr + 1, r});
                }
            }
            cur = tmp;
        }
        return mergeIntervals(cur);
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();
        List<int[]> db = new ArrayList<>();
        while (n-- > 0) {
            String line = sc.nextLine();
            boolean isUndo = line.startsWith("undo");
            String data = isUndo
                ? line.split(" ", 3)[2]
                : line.split(" ", 2)[1];
            List<int[]> segs = parse(data);
            if (isUndo) {
                if (!db.isEmpty()) {
                    db = subtractIntervals(db, segs);
                }
            } else {
                db.addAll(segs);
                db = mergeIntervals(db);
            }
        }
        if (db.isEmpty()) {
            System.out.print(0);
        } else {
            StringJoiner sj = new StringJoiner(",");
            for (int[] seg : db) {
                if (seg[0] == seg[1]) sj.add(String.valueOf(seg[0]));
                else sj.add(seg[0] + "-" + seg[1]);
            }
            System.out.print(sj.toString());
        }
    }
}
        题目内容
在数通设备进行配置下发时,可能会遇到需要下发一个或多个数据段的场景。例如在配置某协议支持的算法类型时,需要下发配置
如 “algorithm 1−10,15−20",用于表明支持的所有算法段范围为 "1−10,15−20"。
为了简化用户操作,数据下发往往同时支持散列及段下发模式,同时对数据段的顺序不做要求,也即允许"algorithm 1−9,10,17−20,15−15”的方式。下发后的数据会整理合并保存在数据库中,合并结果满足以下条件:
1.数据段无法继续合并。
2.数据段从小到大排列。
3.长度为 1 的数据段保存为单个数字形式。
举例来说,"algorithm 1−9,10,17−20,15−15"下发后数据库中储存的数据为“1−10,15,17−20"。
数据合并规则:
- 
在数据库已有数据的情况下,用户新下发的配置不会覆盖已有配置,而是将新增的数据段命并到已有数据中
 - 
例如:数据库中已存在”1−10,15−20"时,再下发"algorithm 5−11"时数据库数据,会合并更新为"1−11 , 15−20"。
 
数据删除规则:
- 
用户下发删除配置时,从已有范围中删除与下发范围重合的范围
 - 
例如:数据库中已存在”1−10,15−20"时,再下发"undo algorithm 5−11,16"时,数据库会更新为"1−4,15,17−20"。 任务:给定一系列配置/删除操作,输出最后的数据库更新结果。
 
输入描述
第一行为 1 个整数 n ,表示下发操作的数量,n<=100
接下来 n 行每行包含 1 个字符串,表明下发的配置/删除操作。配置操作格式为"algorithm 数据",删除操作 "undo algorithm 数据",数据格式如下所示。
字符串数据格式描述:
1.原子数据,单个正整数(“a”)、正整数-正整数("a−b"),其中 a<=b ,每个数的范围为 1−65536
2.字符里组成由原子数据拼接 “,” 组成 ("a−b,c,d−e")
注:
1.用例保证所有输入均合法,输入不保证原子数据排序
2.原子数据段的数量不超过 100
3.数据库中数据为空时,删除操作视为无效操作
输出描述
1 个字符串,表明合并保存后的数据库结果。数据格式与输入中描述字符串数据格式相同。
若最终数据库为空,输出 0
样例1
输入
3
undo algorithm 1-100
algorithm 15-20,1-10
undo algorithm 6,7,8,9-10
输出
1-5,15-20
说明
数据库初始为空
配置 undo algorithm 1−100=>无效,仍为空
配置 algorithm 1−10,15−20=>1−10,15−20
配置 undo algorithm 6,7,8,9−10=>1−5,15−20
样例2
输入
2
algorithm 1-10,15-20
algorithm 5-11
输出
1-11,15-20
说明
数据库初始为空
配置 algorithm 1−10,15−20=>1−10,15−20
配置 algorithm 5−11=>1−11,15−20