解题思路
1. 初始化数据结构
map<string, string> strContent
: 存储字符串类型属性的内容。键是属性名(可能包含通过点.
连接的多级属性名),值是属性的字符串值。vector<string> keyVec
: 存储当前正在处理的属性路径。例如,对于嵌套的对象,这个向量帮助跟踪当前的属性名路径。set<string> objContent
: 存储对象类型属性的集合,只保存对象属性的键名。
2. 解析JSON字符串
ProcessObject
函数用于处理JSON对象,它逐字符读取JSON字符串,识别出键和值,并根据值的类型(字符串或对象)进行相应的处理。- 当发现一个双引号
"
时,会调用GetStr
函数提取双引号之间的字符串,处理转义字符,并返回字符串值。这个返回的字符串可能是键也可能是值,取决于它的上下文位置。 - 当发现一个冒号
:
时,表明接下来的部分是值,此时会调用ProcessPropertyContent
来处理这个值。
3. 处理属性内容
ProcessPropertyContent
函数用来处理和存储属性的内容。这个函数会根据值的类型来决定下一步的操作:- 如果值是字符串(以双引号开头),则将该字符串与当前的属性路径(由
keyVec
构建)关联,并存储到strContent
映射中。 - 如果值是对象(以左花括号
{
开头),则记录这个属性是对象类型(存储到objContent
),并递归调用ProcessObject
来处理这个嵌套对象。
- 如果值是字符串(以双引号开头),则将该字符串与当前的属性路径(由
4. 查询处理
- 主函数最后部分读取查询,并根据查询的键去查找之前解析的JSON数据:
- 如果查询的键对应的是字符串类型的属性,则从
strContent
中获取该属性的值,并以STRING <value>
的格式输出。 - 如果查询的键对应的是对象类型的属性,则检查
objContent
集合,如果存在,则输出OBJECT
。 - 如果查询的键在JSON中不存在,则输出
NOTEXIST
。
- 如果查询的键对应的是字符串类型的属性,则从
总结解题思路
-
初始化和解析: 代码首先初始化用于存储数据的结构,然后逐行读取输入的JSON数据,将其拼接成一个完整的字符串。
-
处理JSON对象: 使用
ProcessObject
函数逐个字符遍历整个JSON字符串,利用GetStr
函数提取出键和字符串值,并根据上下文确定它们是键还是值。对于每个键值对,如果值是字符串,就将它存储在strContent
映射中;如果值是另一个对象,则将键存储在objContent
集合中,并递归地处理这个嵌套对象。 -
构建属性路径: 在解析过程中,使用
keyVec
向量跟踪当前的属性路径(例如,对于嵌套的对象)。这个属性路径用于构建strContent
映射和objContent
集合中的键。 -
回答查询: 最后,对于每个查询,程序检查该属性是否存在于
strContent
或objContent
中,并按照格式要求输出结果。
【100分代码】
#include <iostream>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
map<string, string> strContent; // 存储字符串类型属性内容的映射
vector<string> keyVec; // 存储当前处理路径的键向量
set<string> objContent; // 存储对象类型属性内容的集合
void ProcessPropertyContent(int& i, string& jsonStr); // 函数声明,因为ProcessObject中要调用ProcessPropertyContent
string GetStr(int& i, string& jsonStr) { // 提取双引号之间的字符串,此字符串可能是键,也可能是值
string result = "";
while (++i) {
if (jsonStr[i] == '\\') { // 处理转义字符
result += jsonStr[++i];
}
else if (jsonStr[i] == '"') { // 遇到闭合引号则停止
break;
}
else {
result += jsonStr[i];
}
}
return result;
}
// 处理JSON对象,从start到end之间的字符串
void ProcessObject(int start, int end, string& jsonStr) {
for (int i = start + 1; i < end; ++i) {
if (jsonStr[i] == '"') { // 遇到引号,后面的内容是键
keyVec.push_back(GetStr(i, jsonStr));
}
else if (jsonStr[i] == ':') { // 遇到冒号,后面的内容是值
ProcessPropertyContent(i, jsonStr);
}
}
if (!keyVec.empty()) {
keyVec.pop_back(); // 移除处理完的键
}
}
// 处理属性内容,可以是字符串或对象
void ProcessPropertyContent(int& i, string& jsonStr) {
// 初始化fullKey,开始构建完整的键名
string fullKey;
if (!keyVec.empty()) {
fullKey = keyVec[0]; // 开始时fullKey为第一个键名
for (int j = 1; j < keyVec.size(); ++j) { // 遍历剩余的键名
fullKey += "." + keyVec[j]; // 将键名用点连接起来
}
}
// 解析jsonStr中的属性值
while (++i) {
if (jsonStr[i] == '"') { // 如果属性值是字符串
keyVec.pop_back(); // 从路径中移除当前键
strContent[fullKey] = GetStr(i, jsonStr); // 存储键值对
return;
}
else if (jsonStr[i] == '{') { // 如果属性值是对象
objContent.insert(fullKey); // 记录这是一个对象
int count = 1; // 用于匹配花括号
int j = i; // 记录当前位置
while (count) { // 查找匹配的闭合花括号
j++;
if (jsonStr[j] == '{') count++;
else if (jsonStr[j] == '}') count--;
}
ProcessObject(i, j, jsonStr); // 递归处理嵌套对象
i = j; // 跳过已处理的对象
return;
}
}
}
int main() {
int n, m;
cin >> n >> m;
getchar(); // 消耗换行符
string temp, jsonStr = "";
for (int i = 0; i < n; ++i) {
getline(cin, temp);
jsonStr += temp;
}
ProcessObject(0, jsonStr.size(), jsonStr); // 处理整个JSON字符串
for (int i = 0; i < m; ++i) {
cin >> temp;
if (strContent.count(temp)) // 查询字符串类型的属性
cout << "STRING " << strContent[temp] << endl;
else if (objContent.count(temp)) // 查询对象类型的属性
cout << "OBJECT" << endl;
else // 属性不存在
cout << "NOTEXIST" << endl;
}
}
【70分代码:未处理二级对象结构】
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <string>
#include <algorithm> /
using namespace std;
struct MyObject {
string objName;
map<string, string> strMap;
};
vector<MyObject> objList;
map<string, string> globalStringMap; // 全局字符串map,避免重复定义
void init(string& t) { // 初始化函数,处理转义字符
size_t pos = 0;
while ((pos = t.find("\\\"", pos)) != string::npos) { // 处理转义双引号
t.erase(pos, 1);
}
pos = 0; // 处理转义反斜杠
while ((pos = t.find("\\\\", pos)) != string::npos) {
t.erase(pos, 1);
}
}
void parseJSON(string& text) { // 解析JSON字符串,填充对象列表和全局字符串map
// 预处理输入文本:移除空格和最外层的大括号
text.erase(remove(text.begin(), text.end(), ' '), text.end());
text = text.substr(1, text.length() - 2);
stringstream ss(text);
string token;
bool inString = false;
while (getline(ss, token, ',')) {
// 处理字符串外的逗号分割
if (!inString) {
// 检查是否为对象起始
if (token.find('{') != string::npos) {
// 对象处理逻辑
string objName = token.substr(1, token.find(':') - 2);
init(objName);
// 获取对象内容
size_t pos = token.find('{');
string objContent = token.substr(pos + 1);
objContent.pop_back(); // 移除末尾的}
MyObject newObj;
newObj.objName = objName;
stringstream objStream(objContent);
string pair;
while (getline(objStream, pair, ',')) {
size_t sepPos = pair.find(':');
string key = pair.substr(1, sepPos - 2);
string value = pair.substr(sepPos + 2, pair.length() - sepPos - 3);
init(key);
init(value);
newObj.strMap[key] = value;
}
objList.push_back(newObj);
}
else {
// 全局字符串处理逻辑
size_t sepPos = token.find(':');
string key = token.substr(1, sepPos - 2);
string value = token.substr(sepPos + 2, token.length() - sepPos - 3);
init(key);
init(value);
globalStringMap[key] = value;
}
}
}
}
// 查询逻辑
void query(const string& q) {
if (q.find('.') == string::npos) { // 查询全局字符串或对象名
if (globalStringMap.count(q)) {
cout << "STRING " << globalStringMap[q] << endl;
}
else {
bool found = false;
for (auto& obj : objList) {
if (obj.objName == q) {
cout << "OBJECT\n";
found = true;
break;
}
}
if (!found) cout << "NOTEXIST\n";
}
}
else { // 查询对象内的字符串
auto dotPos = q.find('.');
string objName = q.substr(0, dotPos);
string key = q.substr(dotPos + 1);
for (auto& obj : objList) {
if (obj.objName == objName) {
if (globalStringMap.count(key)) {
cout << "STRING " << globalStringMap[key] << endl;
return;
}
}
}
cout << "NOTEXIST\n";
}
}
int main() {
int n, m;
cin >> n >> m;
getchar(); // 消耗掉换行符
string text, line;
for (int i = 0; i < n; ++i) {
getline(cin, line);
text += line;
}
// 解析JSON字符串
parseJSON(text);
// 查询
for (int i = 0; i < m; ++i) {
string q;
cin >> q;
query(q);
}
return 0;
}
文章部分内容参考自:CSP201709-3JSON查询