华为非AI方向笔试真题 - 迷你文本编辑器的撤销与重做功能

迷你文本编辑器的撤销与重做功能(C++/Py/Java/Js/Go)题解

华为笔试真题 6月17号 非AI方向第二题 200分题型

题目内容

设计一个迷你文本编辑器,支持以下 444 种操作:

  • APPENDAPPENDAPPEND xxx:在文本末尾插入字符串 xxx(xxx 可以是一个或多个字符的连续字符串,无空格和换行)。
  • POPPOPPOP:删除文本末尾最后一个字符,如果文本为空,此操作无效果,不进入 UNDO、REDOUNDO、REDOUNDO、REDO 历史记录。
  • UNDOUNDOUNDO:撤销上一次 APPENDAPPENDAPPEND 或 POPPOPPOP 操作,并恢复文本到该操作之前的状态,可以连续多次撤销多个操作,直至无操作可撤销。
  • REDOREDOREDO:重做上一次被撤销的操作,使文本恢复到撤销前的状态。可以连续多次重做,直至无操作可重做。若在撤销某些操作前执行了新的 APPENDAPPENDAPPEND 或 POPPOPPOP ,则之前的操作记录将被清空。
    初始时文本为空。给定一系列操作,按顺序对文本执行,最终输出文本的内容。

输入描述

第一行输入一个正整数 NNN,表示操作的数量。接下来 NNN 行,每行表示一个操作,格式如下:

  • APPENDAPPENDAPPEND xxx 表示在文本末尾插入字符串 xxx,其中 xxx 由大小写字母、数字或符号组成(不含空格和换行)。
  • POPPOPPOP 表示删除文本末尾最后一个字符。
  • UNDOUNDOUNDO 表示撤销上一次插入或删除操作。
  • REDOREDOREDO 表示重做上一次被撤销的操作。
    注意:
  1. 1≤N≤2000001 \le N \le 2000001≤N≤200000,确保操作数量在合理范围内。
  2. 单次插入的字符串长度不超过 100100100,且所有插入操作的字符总数之和不会超过 200000200000200000。
  3. 操作序列保证合法:UNDOUNDOUNDO 操作不超过可撤销的次数, REDOREDOREDO 操作不超过可重做的次数。

输出描述

输出一个字符串,表示所有操作执行完毕后的文本内容(若最终文本为空,则输出 nothingnothingnothing)。

样例1

输入

复制代码
5
APPEND abc
APPEND d
UNDO
POP
REDO

输出

复制代码
ab

说明

"abcabcabc"−>->−>"abcdabcdabcd"−>->−>"abcabcabc"−>->−>"ababab"−>->−>"ababab"

在撤销某些操作后执行了新的 APPENDAPPENDAPPEND 或 POPPOPPOP,则之前撤销的操作记录将被清空,无法再重做。

样例2

输入

复制代码
5
APPEND ab
UNDO
REDO
UNDO
REDO

输出

复制代码
ab

说明

"ababab"−>->−>""−>->−>"ababab"−>->−>""−>->−>"ababab"

题解

思路

思路:栈模拟

  1. 使用stk存储有效操作,使用undoStk存储撤销操作
  2. 接下来进行模拟每一个操作,具体逻辑如下
    • append时,将append操作插入stk中,并清空undoStk
    • pop时,将pop操作插入stk中,并清空undoStk
    • undo时,如果stk非空时,将stk栈顶操作压入undoStk中,并stk栈顶元素弹出
    • redo时,如果undoStk非空时,将undoStk栈顶元素插入stk中,并将undoStk栈顶元素弹出
  3. 按照2的操作处理之后,stk中就是保留的合法有效操作,按照从前到后模拟stk的操作即可(为了实现从前到后,操作中可以使用数组模拟栈)。

C++

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

// 通用 切割函数 函数 将字符串str根据delimiter进行切割
vector<string> split(const string& str, const string& delimiter) {
    vector<string> result;
    size_t start = 0;
    size_t end = str.find(delimiter);
    while (end != string::npos) {
        result.push_back(str.substr(start, end - start));
        start = end + delimiter.length();
        end = str.find(delimiter, start);
    }
    // 添加最后一个部分
    result.push_back(str.substr(start));
    return result;
}


int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    vector<vector<string>> stk;
    vector<vector<string>> undoStk;
    int n;
    cin >> n;
    cin.ignore();
    
    for (int i = 0; i < n; i++) {
        string input;
        getline(cin, input);
    
        vector<string>ops = split(input, " ");
        if (ops[0] == "APPEND") {
            stk.push_back({ops[0], ops[1]});
            undoStk.clear();
        } else if (ops[0] == "POP") {
            stk.push_back({ops[0]});
            undoStk.clear();
        } else if (ops[0] == "UNDO") {
            if (!stk.empty()) {
                undoStk.push_back(stk.back());
                stk.pop_back(); 
            }
        } else {
            if (!undoStk.empty()) {
                stk.push_back(undoStk.back());
                undoStk.pop_back();
            }
        }
    }
    // 正向模拟结果
    string ans;
    for (int i = 0; i < stk.size(); i++) {
        vector<string> ops = stk[i];
        if (ops[0] == "APPEND") {
            ans += ops[1];
        } else {
            if (ans.empty()) {
                continue;
            }
            ans.pop_back();
        }
    }
    if (ans.empty()) {
        cout << "nothing";
        return 0;
    }
    cout << ans;
    return 0;
}

java

java 复制代码
import java.io.*;
import java.util.*;

public class Main {



    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        List<List<String>> stk = new ArrayList<>();
        List<List<String>> undoStk = new ArrayList<>();

        int n = Integer.parseInt(br.readLine());

        for (int i = 0; i < n; i++) {
            String input = br.readLine();

            String[] ops = input.split(" ");

            if (ops[0].equals("APPEND")) {
                stk.add(Arrays.asList(ops[0], ops[1]));
                undoStk.clear();

            } else if (ops[0].equals("POP")) {
                stk.add(Collections.singletonList(ops[0]));
                undoStk.clear();

            } else if (ops[0].equals("UNDO")) {
                if (!stk.isEmpty()) {
                    undoStk.add(stk.get(stk.size() - 1));
                    stk.remove(stk.size() - 1);
                }

            } else {
                if (!undoStk.isEmpty()) {
                    stk.add(undoStk.get(undoStk.size() - 1));
                    undoStk.remove(undoStk.size() - 1);
                }
            }
        }

        // 正向模拟结果
        StringBuilder ans = new StringBuilder();

        for (int i = 0; i < stk.size(); i++) {
            List<String> ops = stk.get(i);

            if (ops.get(0).equals("APPEND")) {
                ans.append(ops.get(1));
            } else {
                if (ans.length() == 0) {
                    continue;
                }
                ans.deleteCharAt(ans.length() - 1);
            }
        }

        if (ans.length() == 0) {
            System.out.print("nothing");
            return;
        }

        System.out.print(ans);
    }
}

python

python 复制代码
import sys

stk = []
undo_stk = []

n = int(sys.stdin.readline())

for _ in range(n):
    input_str = sys.stdin.readline().rstrip("\n")

    ops = input_str.split(" ")

    if ops[0] == "APPEND":
        stk.append([ops[0], ops[1]])
        undo_stk.clear()

    elif ops[0] == "POP":
        stk.append([ops[0]])
        undo_stk.clear()

    elif ops[0] == "UNDO":
        if stk:
            undo_stk.append(stk[-1])
            stk.pop()

    else:
        if undo_stk:
            stk.append(undo_stk[-1])
            undo_stk.pop()

# 正向模拟结果
ans = []

for ops in stk:
    if ops[0] == "APPEND":
        ans.extend(ops[1])
    else:
        if not ans:
            continue
        ans.pop()

if not ans:
    print("nothing")
else:
    print("".join(ans))

javascript

js 复制代码
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const input = [];

rl.on('line', (line) => {
    input.push(line);
});

rl.on('close', () => {
    let idx = 0;

    const stk = [];
    const undoStk = [];

    const n = Number(input[idx++]);

    for (let i = 0; i < n; i++) {
        const str = input[idx++];

        const ops = str.split(" ");

        if (ops[0] === "APPEND") {
            stk.push([ops[0], ops[1]]);
            undoStk.length = 0;

        } else if (ops[0] === "POP") {
            stk.push([ops[0]]);
            undoStk.length = 0;

        } else if (ops[0] === "UNDO") {
            if (stk.length > 0) {
                undoStk.push(stk[stk.length - 1]);
                stk.pop();
            }

        } else {
            if (undoStk.length > 0) {
                stk.push(undoStk[undoStk.length - 1]);
                undoStk.pop();
            }
        }
    }

    // 正向模拟结果
    const ans = [];

    for (const ops of stk) {
        if (ops[0] === "APPEND") {
            ans.push(...ops[1]);
        } else {
            if (ans.length === 0) {
                continue;
            }
            ans.pop();
        }
    }

    if (ans.length === 0) {
        console.log("nothing");
        return;
    }

    console.log(ans.join(""));
});

Go

go 复制代码
package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func main() {
	in := bufio.NewReader(os.Stdin)

	var n int
	fmt.Fscan(in, &n)

	in.ReadString('\n')

	var stk [][]string
	var undoStk [][]string

	for i := 0; i < n; i++ {
		input, _ := in.ReadString('\n')
		input = strings.TrimRight(input, "\r\n")

		ops := strings.Split(input, " ")

		if ops[0] == "APPEND" {
			stk = append(stk, []string{ops[0], ops[1]})
			undoStk = undoStk[:0]

		} else if ops[0] == "POP" {
			stk = append(stk, []string{ops[0]})
			undoStk = undoStk[:0]

		} else if ops[0] == "UNDO" {
			if len(stk) > 0 {
				undoStk = append(undoStk, stk[len(stk)-1])
				stk = stk[:len(stk)-1]
			}

		} else {
			if len(undoStk) > 0 {
				stk = append(stk, undoStk[len(undoStk)-1])
				undoStk = undoStk[:len(undoStk)-1]
			}
		}
	}

	// 正向模拟结果
	var ans []byte

	for _, ops := range stk {
		if ops[0] == "APPEND" {
			ans = append(ans, ops[1]...)
		} else {
			if len(ans) == 0 {
				continue
			}
			ans = ans[:len(ans)-1]
		}
	}

	if len(ans) == 0 {
		fmt.Print("nothing")
		return
	}

	fmt.Print(string(ans))
}