最近遇到了一道很有意思的面试题目,属于算法题的类型,但是确不是 leetcode 的原题,特此记录思考过程与解答方法。
题目描述
输入一个 protobuf 类型的字符串,如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| syntax = "proto"; package webcast.api.user;
message ReportReasonRequestParams { int64 room_id = 1; int64 ab_scene = 2; }
message ReportReasonResponse{ message ReasonData { int64 reason = 1; string reason_str = 2; } repeated ReasonData data = 1; Extra extra=2; }
|
输出仍然是一个字符串,要求把嵌套的结构拉出来铺到外层。如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| syntax = "proto"; package webcast.api.user;
message ReportReasonRequestParams { int64 room_id = 1; int64 ab_scene = 2; }
message ReasonData { int64 reason = 1; string reason_str = 2; }
message ReportReasonResponse{ repeated ReasonData data = 1; Extra extra=2; }
|
思路
这个问题很容易联想到类似的问题,比如解析 json 字符串,有效的括号数目,那么就一般就需要识别一些关键的特征,比如”{“,以及利用栈这样的数据结构来存储数据(因为栈有先入后出的特性,那么里层的嵌套结构就会后入,然后先从栈中弹出来)。
但是这里值得注意的是,我们需要把特征定为 message xx {
而不是简单的 {
, 因为提取的时候,message
关键字也是提取数据的一个整体。
所以我想到的是,使用正则表达式,先把所有的特征提取出来,记录特征字符串和它的索引,然后遍历整个字符串,在遇到一个特征字符串的时候,入栈,在遇到}
的时候,将栈顶元素弹出,拼接到结果字符串中,在遇到其他字符的时候,将它拼接到栈顶元素中。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| function parse(str) { let res = ""; const stack = [""];
const reg = /message.*{/g; let flags = []; let temp = null; while ((temp = reg.exec(str)) !== null) { flags.push([temp[0], temp.index]); }
for (let i = 0; i < str.length; i++) { if (flags.length && i === flags[0][1]) { const [str] = flags.shift(); stack.push([str]); i = i + str.length - 1; } else if (str[i] === "}") { let top = stack.pop(); top = top + str[i] + (str[++i] ?? ""); res += top; } else { stack[stack.length - 1] = stack[stack.length - 1] + str[i]; } }
if (stack.length) { res = stack.pop() + res; }
return res; }
const str = `syntax = "proto"; package webcast.api.user;
/* METHOD: GET */ message ReportReasonRequestParams { int64 room_id = 1; // different reasons for different roo int64 ab_scene = 2; // ab scene, deprecated }
message ReasonData { int64 reason = 1; string reason_str = 2; }
message ReportReasonResponse{ repeated ReasonData data = 1; Extra extra=2; }`;
console.log(parse(str));
|
输出为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| syntax = "proto"; package webcast.api.user;
message ReportReasonRequestParams { int64 room_id = 1; int64 ab_scene = 2; } message ReasonData { int64 reason = 1; string reason_str = 2; } message ReportReasonResponse{ repeated ReasonData data = 1; Extra extra=2; }
|