例题6-17 UVA10562 Undraw the Trees(29行AC代码)

紫书刷题进行中,题解系列【GitHub|CSDN

例题6-17 UVA10562 Undraw the Trees(29行AC代码)

题目大意

本题理解题目很关键

给定ascii字符表示的多叉树,输出指定的括号表达形式。

其中存在共用孩子结点情况和重复结点名情况,如下图所示

  • 共用孩子结点:B的孩子是C和E,D的孩子也是C和E
  • 重复结点名:第一个结点A和最后一个同名,但输出时照输不误
A
|
--
BD
||
--
CE
|
A

思路分析

最直接的思路是解析字符串,构造多叉树,先序遍历输出,从多叉树到输出转换很容易,但是解析字符串较为繁琐,因此问自己一个问题:一定要构建多叉树吗?

先看直接思路,若是解析字符串,必须通过|--等字符间的位置关系来确定孩子和父亲关系,但转念一想,我能够解析这些关系,说明我已经能够得到字符之间的位置关系了,那直接把整棵树存在一个二维数组中,不就可以随时获取字符间位置关系了吗?

通过以上分析,发现可以省略实际建树的过程,在分析字符间关系时顺便输出结果,代码也极为简短

之后会给出两种思路对于的代码,可对比分析优劣

注意点

  • 注意访问数组的元素时先判断是否越界,因为本题输入中每一行宽度不一定相同

  • scanf和fgets/getline混用时,注意吸收多余换行

  • 空树特判

AC代码(C++11)

二维数组递归输出(29行)

#include<bits/stdc++.h>
using namespace std;
int T;
vector<string> buf; // 存储输入的树
void dfs(int r, int c) { // 递归打印以buf(r,c)为根的子树
    printf("%c", buf[r][c]);
    if (r < buf.size()-1 && buf[r+1][c] == '|') { // 有孩子
        putchar('('); int l=c;
        while (buf[r+2][l] == '-') l--; // 查找--的左边界
        for (int i=l+1; i < buf[r+2].size() && buf[r+2][i] == '-'; i ++) {
            if (i < buf[r+3].size() && buf[r+3][i] != ' ') dfs(r+3, i); // 注意r+3的有效范围判断
        }
        putchar(')');
    }
    else printf("()");
}
int main() {
    scanf("%d", &T); getchar(); // 吸收多余换行
    string s;
    while (T --) {
        buf.clear();
        while (getline(cin, s) && s != "#") buf.push_back(s); // 以数组存储树
        putchar('(');
        for (int i=0; !buf.empty() && i < buf[0].size(); i ++) 
            if (buf[0][i] != ' ') {dfs(0,i); break;} // 找到根
        puts(")");
    }
    return 0;
}

字符串解析建树(51行)

#include<bits/stdc++.h>
using namespace std;
int T;
void dfs(map<int,vector<int> >&tree, vector<char>& dict, int root=0) { // 根据多叉树递归打印
    if (dict.size() > root) printf("%c", dict[root]);
    if (!tree[root].empty()) {
        printf("(");
        for (auto t : tree[root]) dfs(tree, dict, t);
        printf(")");
    }
    else printf("()");
}
int main() {
    scanf("%d", &T); getchar(); // 吸收多余换行
    string s[4]; // 4行:根,|,-,孩子
    while (T --) {
        map<int,vector<int> > tree; // 多叉树
        vector<char> dict; // 结点字符字典(给每个字符进行编号,处理重复字符)
        map<int,int> id; // 每个位置->对应字符的id
        getline(cin, s[0]);
        if (s[0][0] == '#') {printf("()\n"); continue;} // 空树
        auto p = find_if(s[0].begin(), s[0].end(), [](char& ch){return ch != ' ';});
        id[p - s[0].begin()] = dict.size(); dict.push_back(*p);
        while (getline(cin, s[1]) && s[1][0] != '#') {
            getline(cin, s[2]); getline(cin, s[3]); 
            vector<int> a[3]; // a[1]表示|所在位置,a[2]表示每段-的起止位置
            for (int i=0; i < s[1].size(); i ++) if (s[1][i] == '|') a[1].push_back(i); // |的位置
            int flag=0;
            for (int i=0; i < s[2].size(); i ++) { // -分割区间:[起始,结束)
                if (flag == 0 && s[2][i] == '-') a[2].push_back(i), flag=1; // 起点
                else if (flag == 1 && s[2][i] == ' ') a[2].push_back(i), flag=0; // 终点
            }
            if (a[2].size() % 2 != 0) a[2].push_back(s[2].size()); // 最后一段-
            map<int,int> tid; // 临时存储
            for (int i=0; i < s[3].size(); i ++) { // 孩子处理
                if (s[3][i] != ' ') {
                    for (int j=0; j < a[2].size(); j += 2) // 查询在哪一段
                        if (a[2][j] <= i && i < a[2][j+1]) {
                            tid[i] = dict.size(); dict.push_back(s[3][i]);
                            for (int k=0; k < a[1].size(); k ++) { // 查询符合区间的父结点
                                if (a[2][j] <= a[1][k] && a[1][k] < a[2][j+1]) tree[id[a[1][k]]].push_back(dict.size()-1);
                            }
                        }
                }
            }
            id = tid; s[0] = s[3]; // 更新
        }
        putchar('('); dfs(tree, dict, 0); puts(")");
    }
    return 0;
}
发布了128 篇原创文章 · 获赞 87 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_40738840/article/details/104381798
今日推荐