#P3299. 第2题-变量生命周期计算
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 314
            Accepted: 86
            Difficulty: 7
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2025年6月25日-暑期实习
                              
                      
          
 
- 
                        算法标签>栈          
 
第2题-变量生命周期计算
题目描述
在Rust语言中,变量的生命周期与其作用域绑定,退出作用域时自动销毁。我们用一个简化模型来模拟这一过程:
- 
“{” 和 “}” 表示新开和结束一个作用域;
 - 
每行要么是
let name:在当前作用域声明变量name,drop name:在当前作用域手动销毁变量name。
 - 
离开一个作用域时,若有未被
$drop$的变量,则自动按“后声明先销毁”顺序销毁。 
出错情况:
1. 销毁了一个未在同一作用域中声明的变量;
2. 已声明的变量被 drop 超过一次。
输出要求:按销毁顺序依次输出变量名,用空格分隔;若出错,则输出已成功销毁的变量,后跟一个空格和字符串 "Error",然后终止。
思路
- 
使用一个栈来管理作用域,每个作用域对应一个栈帧,栈帧内部维护一个按声明顺序的列表。
 - 
维护一个全局哈希表,记录每个变量所属的栈帧层级以及是否已被销毁。
 - 
逐行处理输入:
- 
遇到
{,压入新的空栈帧; - 
遇到
},弹出当前栈帧,按逆序自动销毁其中所有未被drop的变量; - 
遇到
let name,将$name$加入当前栈帧尾部,并在哈希表中标注层级及“未销毁”; - 
遇到
drop name,检查哈希表:- 若不存在或层级不匹配,则报错;
 - 若已销毁,则报错;
 - 否则标记为已销毁,输出该变量。
 
 
 - 
 - 
若中途报错,停止处理并输出
"Error";否则最终完成所有行后输出结果。 
C++
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int N;
    cin >> N;              // 读取总行数 N
    string line;
    vector<vector<string>> scopeStack;  // 作用域栈
    unordered_map<string, pair<int,bool>> info; // 变量 -> (层级, 已销毁)
    vector<string> output;  // 记录销毁顺序
    bool error = false;
    getline(cin, line); // 吸收行末
    for (int i = 0; i < N; i++) {
        getline(cin, line);
        // 去除前导空格
        int pos = line.find_first_not_of(' ');
        if (pos == string::npos) continue;
        line = line.substr(pos);
        if (line == "{") {
            // 新作用域
            scopeStack.emplace_back();
        }
        else if (line == "}") {
            // 结束当前作用域,自动销毁未 drop 的变量
            int lvl = scopeStack.size() - 1;
            auto &frame = scopeStack.back();
            for (int k = frame.size() - 1; k >= 0; k--) {
                const string &v = frame[k];
                if (!info[v].second) {
                    output.push_back(v);
                    info[v].second = true;
                }
            }
            scopeStack.pop_back();
        }
        else if (line.rfind("let ", 0) == 0) {
            // let 操作
            string var = line.substr(4);
            int lvl = scopeStack.size() - 1;
            scopeStack.back().push_back(var);
            info[var] = {lvl, false};
        }
        else if (line.rfind("drop ", 0) == 0) {
            // drop 操作
            string var = line.substr(5);
            if (!info.count(var) || info[var].first != (int)scopeStack.size() - 1) {
                // 未声明或不在本层
                error = true;
            } else if (info[var].second) {
                // 重复 drop
                error = true;
            } else {
                // 合法 drop
                output.push_back(var);
                info[var].second = true;
            }
        }
        if (error) {
            break;
        }
    }
    // 输出结果
    for (auto &v : output) {
        cout << v << ' ';
    }
    if (error) {
        cout << "Error";
    }
    return 0;
}
Python
import sys
def main():
    N = int(sys.stdin.readline().strip())  # 读取总行数 N
    scope_stack = []  # 作用域栈,每层是变量列表
    info = {}        # 变量 -> (层级, 已销毁)
    output = []
    error = False
    for _ in range(N):
        line = sys.stdin.readline().lstrip()
        if line.startswith('{'):
            scope_stack.append([])
        elif line.startswith('}'):
            lvl = len(scope_stack) - 1
            frame = scope_stack.pop()
            # 自动销毁
            for var in reversed(frame):
                if not info[var][1]:
                    output.append(var)
                    info[var] = (lvl, True)
        elif line.startswith('let '):
            var = line.split()[1]
            lvl = len(scope_stack) - 1
            scope_stack[-1].append(var)
            info[var] = (lvl, False)
        elif line.startswith('drop '):
            var = line.split()[1]
            if var not in info or info[var][0] != len(scope_stack) - 1:
                error = True
            elif info[var][1]:
                error = True
            else:
                output.append(var)
                info[var] = (info[var][0], True)
        if error:
            break
    # 打印输出
    print(' '.join(output), end='')
    if error:
        print('Error')
if __name__ == '__main__':
    main()
Java
import java.io.*;
import java.util.*;
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine().trim()); // 读取总行数 N
        List<List<String>> scopeStack = new ArrayList<>();  // 作用域栈
        Map<String, Pair> info = new HashMap<>();           // 变量 -> (层级, 已销毁)
        List<String> output = new ArrayList<>();
        boolean error = false;
        for (int i = 0; i < N; i++) {
            String line = br.readLine().replaceAll("^\\s+", "");
            if (line.equals("{")) {
                scopeStack.add(new ArrayList<>());
            } else if (line.equals("}")) {
                int lvl = scopeStack.size() - 1;
                List<String> frame = scopeStack.remove(lvl);
                // 自动销毁
                for (int k = frame.size() - 1; k >= 0; k--) {
                    String var = frame.get(k);
                    Pair p = info.get(var);
                    if (!p.destroyed) {
                        output.add(var);
                        p.destroyed = true;
                    }
                }
            } else if (line.startsWith("let ")) {
                String var = line.substring(4);
                int lvl = scopeStack.size() - 1;
                scopeStack.get(lvl).add(var);
                info.put(var, new Pair(lvl, false));
            } else if (line.startsWith("drop ")) {
                String var = line.substring(5);
                if (!info.containsKey(var) || info.get(var).level != scopeStack.size() - 1) {
                    error = true;
                } else if (info.get(var).destroyed) {
                    error = true;
                } else {
                    output.add(var);
                    info.get(var).destroyed = true;
                }
            }
            if (error) break;
        }
        // 输出结果
        for (String v : output) {
            System.out.print(v + " ");
        }
        if (error) {
            System.out.print("Error");
        }
    }
    // 存储变量信息的辅助类
    static class Pair {
        int level;
        boolean destroyed;
        Pair(int level, boolean destroyed) {
            this.level = level;
            this.destroyed = destroyed;
        }
    }
}
        题目内容
在 Rust 语言中,一个变量的生命周期是与其变量的作用域绑定的,变量离开作用域时被自动销毁。
这里给出一个简单的模型;一对大括号为一个生命周期,除了括号之外,每一行包含以下两个操作之一:
let {var_ name} drop {var _ name}
其中:let 表示声明一个变量,{var_ name} 为变量名;drop 表示手动销毁一个变量,销毁的对象为变量名 {var_name} 。
其它规则:离开一个作用域时,如果有变量没有被drop显式销毁,则自动销毁,顺序规则是后声明的先销毁。
出错情况:
1.销毁了一个没有声明的变量。
2.一个已经声明的变量被手动 drop 超过一次。
输入描述
第一行为一个数字 N ,表示后面待输入代码的行数。
其中:5<=N<=15000 。
{ 独占一行
} 独占一行
let 和变量名之间只有一个空格
drop 和变量名之间只有一个空格
变量名只包含大小写字母,长度为 [1,32]
输入约束:
1.全局每一行 let 声明变量的变量名都不相同。
2.用例中不会出现 drop 非同一作用域变量(包括父作用域)的情况。
3.除输入的第一行为行数,输入的第二行一定会是 '{' ,最后一行一定是'}' :即一定会显式声明一个最外层作用域。
4.每一行输入的行头可能有 0 个或者多个前导空格,let/drop 与变量名之间只有一个空格。
5.不会出现括号不成对匹配、变量名不符号要求等输入异常的场景,编码时不需要考虑括输入不正确的场景。
输出描述
输出各变量的销毁顺序,以一个空格分隔
如果发生出错的情况,则将出错前成功释放的变量全部按要求打印出来之后,再接一个空格和一个固定字符串 “Error”,然后中止输出。
样例1
输入
6
{
 let a
 let b
 let c
 drop b
}
输出
b c a
说明
按行说明:
待输入的总代码行数为 6
新增第一个作用域
第一个作用域声明变量 a
第一个作用域声明变量 b
第一个作用域声明变量 c
显式销毁 b
离开第一个作用域,按声明反序,销毁 ca (因为 b 已经显式销毁)
样例2
输入
9
{
 let a
 let b
 {
  let c
 }
 drop b
 drop d
}
输出
c b Error
说明
按行说明:
待输入的总代码行数为 9
新增第一个作用域
第一个作用域声明变量 a
第一个作用域声明变量 b
新增第二个作用域
第二个作用域声明变量 c
离开第二个作用域,按声明反序,自动销毁 c
显式销毁 b
显式销毁 d ,但是 d 在其所属的第一个作用域中还没有声明,报错退出
样例3
输入
12
{
 let a
 let b
 {
  let c
  {
   let d
   let f
  }
 }
 drop b
}
输出
f d c b a
说明
按行说明:
待输入的总代码行数为 12
新增第一个作用域
第一个作用域声明变量 a
第一个作用域声明变量 b
新增第二个作用域
第二个作用域 声明变量 c
新增第三个作用域
第三个作用域 声明变量 d
第三个作用域 声明变量 f
离开第三个作用域,按声明反序,销毁 f d
离开第二个作用域,按声明反序,销毁 c
显式销毁变量 b
离开第一个作用,按声明反序,销毁 a (因为b已经显式销毁)
样例4
输入
7
{
 let a
 {
  drop a
 }
}
输出
Error
说明
按行说明:
待输入的总代码行数为 7
新增第一个作用域
第一个作用域声明变量 a
新增第二个作用域
drop 变量 a ,但是 a 非本作用域变量(为其父作用域变量),出错。