迷你文本编辑器的撤销与重做功能(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≤N≤2000001 \le N \le 2000001≤N≤200000,确保操作数量在合理范围内。
- 单次插入的字符串长度不超过 100100100,且所有插入操作的字符总数之和不会超过 200000200000200000。
- 操作序列保证合法: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"
题解
思路
思路:栈模拟
- 使用
stk存储有效操作,使用undoStk存储撤销操作 - 接下来进行模拟每一个操作,具体逻辑如下
append时,将append操作插入stk中,并清空undoStkpop时,将pop操作插入stk中,并清空undoStkundo时,如果stk非空时,将stk栈顶操作压入undoStk中,并stk栈顶元素弹出redo时,如果undoStk非空时,将undoStk栈顶元素插入stk中,并将undoStk栈顶元素弹出
- 按照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))
}