CCF认证-202409-03 | 补丁应用(未通过)

前言:

        这道题难度过于夸张?并且根据过往的CCFCSP认证第三题的通过率,我就没奔着通过去写,只是想测试一下定时1小时能摸到几分。这道题用时1小时28分钟(带完善),分数0。

        所以CCF认证时在没有走投无路或者有80%以上把握就千万不能做这道题,第四题都比这个要简单(如果实力允许)。但是话说回来,我也不是一无是处,样例都能过,也完善了一些细节,在这个35届CCF题目解答还没大面积传开的节点,就先放在这做一个参考。(反正我没找到这道题有AC的)

题目背景

西西艾弗岛运营公司的信息技术部门,需要协作开展程序开发和代码审查工作。他们的工作流程是这样的: 首先,开发者将代码复制一份,并修改代码副本,从而得到期望的代码。然后,开发者使用 diff 工具比较修改前后的代码的区别,并将其输出用邮件发送给代码审查者。审查者收到邮件后, 可以直接观察开发者作出的代码变更,并提出意见。反复进行后,当代码审查者对变更满意时, 会使用 patch 程序,将开发者提出的修改应用到原代码上,从而得到最终的代码。

现在,他们已经可以实现 diff 程序,但是需要你帮助他们实现这个 patch 程序。

diff 的输出称作补丁。补丁由一个或多个块组成。每个块包含若干行文本,表示对文件的一处修改。 其中第一行以 @@ 开头和结尾,形如:

@@ -NN,MM +nn,mm @@

其中 NNMMnnmm 表示一个 1 至 9 之间的字符和零个或多个 0 至 9 之间的字符组成的字符串, 表示一个正整数。每块的第一行表示原文件和新文件的行号范围。其中,NN 表示这处修改在原文件的第 NN 行开始(原文件的行号从 1 开始编号); MM 表示这处修改涉及原文件的 MM 行;nn 表示这处修改,在修改后从新文件的第 nn 行开始; mm 表示这处修改在修改后,在新文件中有 mm 行。

随后会有若干行文本,表示修改的内容。如果一行文本以 - 开头,表示这行文本在原文件中被删除; 如果一行文本以 + 开头,表示这行文本在新文件中被添加;如果一行文本以空格开头, 表示这行文本在原文件和新文件中都存在,未发生变化。因此,一个块中所有以 - 开头的行和以空格开头的行的总数,应该等于 MM;一个块中所有以 + 开头的行和以空格开头的行的总数,应该等于 mm。一处修改的描述中,可以适当包含若干不变的行, 以便确定修改的上下文。

例如,下面是一个块的内容:

@@ -1,4 +1,5 @@
-a
+b
 1
+c
 2
 3

表示该处修改自原文件的第 1 行开始,共 4 行;修改后的文本从新文件的第 1 行开始,共 5 行。 此处修改前,原文件的内容应该为:

a
1
2
3

修改后,新文件的内容应该为:

b
1
c
2
3

题目描述

但是,patch 程序在处理 diff 的输出时,对格式的要求可以较为宽松。例如, 它可以允许块内有注释,也可以允许块的行号与实际原文件的行号不匹配。这是因为, 在实际应用中,diff 生成后,源文件可能经历了其它的变更,导致行号出现了挪动。patch 程序的具体工作过程是:

  1. 读取全部输入,将 # 开头的行视为注释,并移除;
  2. 寻找 @ 开头的行,并将该行至下一个 @ 开头的行(或文本结尾)之间的内容视为一个块,如果没有找到 @ 开头的行,则认为补丁损坏;
  3. 从前到后依次对每个块:
  4. 解析第一行,检查其格式是否正确,如果不正确,则认为补丁损坏;
  5. 解析出 NNMMnnmm,其中忽略 nn
  6. 如果这个块不是第一个块,检查 NN 是否不小于前一个块的 NN 与 MM 之和,如果不是,则认为补丁损坏;
  7. 解析其余行,如果这些行中存在不是以 -+、空格开头的行,则认为补丁损坏;
  8. 将块中所有以 - 开头的行和以空格开头的行提取出来,作为原文件的内容片段;
  9. 检查原文件的内容片段的行数是否与 MM 一致,如果不一致,则认为补丁损坏;
  10. 将块中所有以 + 开头的行和以空格开头的行提取出来,作为新文件的内容片段;
  11. 检查新文件的内容片段的行数是否与 mm 一致,如果不一致,则认为补丁损坏;
  12. 如果所有块都通过了检查,则对于每个块:
  13. 检查是否存在绝对值小于 MM 的整数 δδ,使得自原文件的第 NN + δδ 行开始的 MM 行, 与块的原文件内容片段完全匹配。如果不是第一个块,还需满足 NN + δδ 不小于前一个块的 NN 与 MM 之和,即满足每块对应的原文区域没有重叠。如果不存在这样的 δδ,则认为补丁损坏;
  14. 如果存在多个这样的 δδ,则取绝对值最小的那个,如果仍然存在多个,则取最小的那个;
  15. 将原文件的第 NN + δδ 行开始的 MM 行替换为块的新文件内容片段;
  16. 将该块和此后的所有块的 NN 加上 δδ。

输入格式

从标准输入读入数据。

输入的第一行包含一个正整数 nn,表示原文件的总行数。

接下来的 nn 行文本,表示原文件的内容。

接下来的若干行,表示待应用的补丁,其中补丁可能是损坏的。

输出格式

输出到标准输出。

输出应用补丁后的文件内容;如果补丁损坏,输出 Patch is damaged.

样例1输入

7
bbb
a
1
2
3
4
5
dummy
@@ -1,4 +1,5 @@
-a
+b
 1
+c
 2
 3
@@ -6,2 +6,2 @@
-4
+6
 5

样例1输出

bbb
b
1
c
2
3
6
5

样例1解释

输入原始文本共有 7 行,之后开始解析补丁。遇到的第一个以 @ 开头的行是 @@ -1,4 +1,5 @@,表示第一个块内容的开始,因此忽略此前的 dummy 行,即输入的第 8 行。第一个块即为题目描述中的块, 该块的原始内容与输入的原始内容的第 2 行到第 5 行相同,因此对应将这些行替换,且将该块及其后的所有块的行号加上 1。所以此时输出 bbb(未包括于第一个块)、b1c23。第二个块的头部标识该块起始于原始文件的第 6 行,加上 1 为第 7 行,该块的原始内容共两行,分别为 45。这一内容与原始文件的第 6 行、第 7 行相同,因此对应将这两行替换,且将该块及其后的所有块的行号减去 1。所以此时接下来输出 65

样例2输入

7
bbb
a
1
2
3
4
5
dummy

样例2输出

Patch is damaged.

样例2解释

输入原始文本共有 7 行,之后开始解析补丁。补丁中不含有有效的块,因此为非法补丁。

样例3输入

8
bbb
a
1
2
3
4
5
6
dummy
@@ -1,4 +1,5 @@
-a
+b
 1
+c
 2
 3
@@ -6,2 +6,2 @@
-4
+6
 5
 6

样例3输出

Patch is damaged.

样例3解释

输入原始文本共有 8 行,之后开始解析补丁。补丁的第二个块中,其第一行表示其原始内容有 2 行,但随后却给出了 3 行的原始内容,因此为非法补丁。

样例4输入

8
bbb
a
1
2
3
4
5
6
dummy
@@ -1,4 +1,5 @@
-a
+b
 1
+c
 2
 3
@@ -6,2 +6,2 @@
-4
+6
 5
# 6

样例4输出

bbb
b
1
c
2
3
6
5
6

样例4解释

与样例 3 相比,多出来的一行前增加了 # 号被忽略,因此为合法补丁,其应用过程与样例 1 类似。

子任务

对于 30% 的数据,补丁仅包含一个块,且不含有注释。

对于 60% 的数据,有 n≤60n≤60,且输入中行的长度不超过 120,补丁是合法补丁,补丁块相对于原始内容无偏移。

对于 100% 的数据,有 n≤2000n≤2000,且输入中行的长度不超过 830,且输入的总长度不超过 500KiB,输入数据中仅包含 ASCII 码在 32 至 126 之间的字符和换行符(ASCII 码为 10),且补丁块不超过 25 个。

评分方式

本题共包括十个测试点:

  • 前六个测试点每通过一个可得 1010 分;

  • 后四个测试点同时通过可得 4040 分,否则不得分。换言之,对所有测试点皆输出补丁损坏不会得分。

提示

本题目的输入数据中,每一行,包括最后一行在内,都以换行符(ASCII 码为 10,即 16 进制的 0x0a,亦为 escape 序列 \n)结束。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iomanip>
#include<queue>
#include<stack>
#include<vector>
#include<unordered_set>
#include<unordered_map>
#include<map>
#include<set>
using namespace std;
typedef long long int ll;

int n=0,i,j;
vector<string> vt;
string s,s_next="";
int is_damaged=0;
int plus_num=0;
int block_num=0;
int lastNN=0,lastMM=0;

void solution(string s){
    vector<string> old;
    vector<string> news;
    int i,j;
    string st;
    int NN=0,MM=0,nn=0,mm=0;
    //check string @@
    string a="";
    for(i=0;i<s.size();i++){
        if(s[i]!=' '){
            a+=s[i];
        }
        else{
            if(a=="@@"){
                a="";
                continue;
            }
            else if(a[0]=='-'){
                int is_NN=1;
                for(j=0;j<a.size();j++){
                    if(is_NN && a[j]>='0' && a[j]<='9'){
                        NN*=10;
                        NN+=(a[j]-48);
                    }
                    else if(a[j]==','){
                        is_NN=0;
                    }
                    else if(a[j]>='0' && a[j]<='9'){
                        MM*=10;
                        MM+=(a[j]-48);
                    }
                }
            }
            else if(a[0]=='+'){
                int is_nn=1;
                for(j=0;j<a.size();j++){
                    if(is_nn && a[j]>='0' && a[j]<='9'){
                        nn*=10;
                        nn+=(a[j]-48);
                    }
                    else if(a[j]==','){
                        is_nn=0;
                    }
                    else if(a[j]>='0' && a[j]<='9'){
                        mm*=10;
                        mm+=(a[j]-48);
                    }
                }
            }
            else{
                is_damaged=1;
                cout << "Patch is damaged." << "\n";
                return;
            }
            a="";
        }
    }

    //check(not the first block)
    if(block_num!=1){
        if(NN<lastMM+lastNN){
            is_damaged=1;
            cout << "Patch is damaged." << "\n";
            return;
        }
    }
    //cout << NN << " " << MM << " " << nn << " "  << mm << "\n";
    //block input
    while(true){
        if(getline(cin,a)){
            if(a[0]=='@'){
                s_next=a;
                break;
            }
            else if(a[0]=='+' || a[0]=='-' || a[0]==' '){
                if(a[0]=='-'){
                    old.push_back(a.substr(1,a.size()-1));
                }
                else if(a[0]=='+'){
                    news.push_back(a.substr(1,a.size()-1));
                }
                else if(a[0]==' '){
                    old.push_back(a.substr(1,a.size()-1));
                    news.push_back(a.substr(1,a.size()-1));
                }
            }
            else if(a[0]=='#'){
                continue;
            }
            else{
                is_damaged=1;
                cout << "Patch is damaged." << "\n";
                return;
            }
        }
        else{
            break;
        }
    }
    //check NNMM
    if(old.size()!=MM || news.size()!=mm){
        is_damaged=1;
        cout << "Patch is damaged." << "\n";
        return;
    }
    /*
    cout << "------------------" << "\n";
    for(i=0;i<old.size();i++){
        cout << old[i] << " ";
    }
    cout << "\n";
    for(i=0;i<news.size();i++){
        cout << news[i] << " ";
    }
    cout << "\n";
    cout << "------------------" << "\n";
    */
    //found
    NN+=plus_num;
    int is_p=0;
    int t=0;
    for(i=NN;i<vt.size()-old.size()+1;i++){
        for(j=0;j<old.size();j++){
            if(vt[i+j]==old[j] && j==old.size()-1){
                is_p=1;
            }
            else if(vt[i+j]==old[j]){
                continue;
            }
            else{
                break;
            }
        }
        if(is_p){
            t=i;
            plus_num+=(t-NN);
            break;
        }
        if(i==vt.size()-old.size()){
            is_damaged=1;
            cout << "Patch is damaged." << "\n";
            return;
        }
    }
    //check(not first)
    if(block_num!=1){
        if(t<lastMM+lastNN){
            is_damaged=1;
            cout << "Patch is damaged." << "\n";
            return;
        }
    }
    //change
    for(i=0;i<old.size();i++){
        vt.erase(vt.begin()+t);
    }
    for(i=news.size()-1;i>=0;i--){
        vt.insert(vt.begin()+t,news[i]);
    }
    /*
    for(i=1;i<vt.size();i++){
        cout << vt[i] << "\n";
    }
    */
    //recordNN MM
    lastMM=MM;
    lastNN==NN;
}

main(){
    cin >> n ;
    vt.push_back("");
    while(n>0){
        cin >> s;
        if(s[0]=='#'){
            continue;
        }
        vt.push_back(s);
        n--;
    }
    int is_in=0;
    //getchar();
    while(true){
        if(getline(cin,s)){
            while(s[0]=='@'){
                is_in=1;
                block_num++;
                solution(s);
                if(s_next!=""){
                    s=s_next;
                    s_next="";
                    continue;
                }
                s="";
                break;
            }
        }
        else{
            break;
        }
    }
    if(!is_in){
        is_damaged=1;
        cout << "Patch is damaged." << "\n";
        return 1;
    }
    //cout << vt[1] << "\n";
    if(!is_damaged){
        for(i=1;i<vt.size();i++){
            cout << vt[i] << "\n";
        }
    }

}

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值