(CCF CSP 2017年9月3日) JSON查詢+詳細分析
JSON (JavaScript Object Notation) 是一種輕量級的資料交換格式,可以用來描述半結構化的資料。JSON 格式中的基本單元是值 (value),出於簡化的目的本題只涉及 2 種類型的值: * 字串 (string):字串是由雙引號 " 括起來的一組字元(可以為空)。如果字串的內容中出現雙引號 ",在雙引號前面加反斜槓,也就是用 \" 表示;如果出現反斜槓 \,則用兩個反斜槓 \\ 表示。反斜槓後面不能出現 " 和 \ 以外的字元。例如:""、"hello"、"\"\\"。 * 物件 (object):物件是一組鍵值對的無序集合(可以為空)。鍵值對錶示物件的屬性,鍵是屬性名,值是屬性的內容。物件以左花括號 { 開始,右花括號 } 結束,鍵值對之間以逗號 , 分隔。一個鍵值對的鍵和值之間以冒號 : 分隔。鍵必須是字串,同一個物件所有鍵值對的鍵必須兩兩都不相同;值可以是字串,也可以是另一個物件。例如:{}、{"foo": "bar"}、{"Mon": "weekday", "Tue": "weekday", "Sun": "weekend"}。 除了字串內部的位置,其他位置都可以插入一個或多個空格使得 JSON 的呈現更加美觀,也可以在一些地方換行,不會影響所表示的資料內容。例如,上面舉例的最後一個 JSON 資料也可以寫成如下形式。 { "Mon": "weekday", "Tue": "weekday", "Sun": "weekend" } 給出一個 JSON 格式描述的資料,以及若干查詢,程式設計返回這些查詢的結果。
輸入格式
第一行是兩個正整數 n 和 m,分別表示 JSON 資料的行數和查詢的個數。 接下來 n 行,描述一個 JSON 資料,保證輸入是一個合法的 JSON 物件。 接下來 m 行,每行描述一個查詢。給出要查詢的屬性名,要求返回對應屬性的內容。需要支援多層查詢,各層的屬性名之間用小數點 . 連線。保證查詢的格式都是合法的。
輸出格式
對於輸入的每一個查詢,按順序輸出查詢結果,每個結果佔一行。 如果查詢結果是一個字串,則輸出 STRING <string>,其中 <string> 是字串的值,中間用一個空格分隔。 如果查詢結果是一個物件,則輸出 OBJECT,不需要輸出物件的內容。 如果查詢結果不存在,則輸出 NOTEXIST。
樣例輸入
10 5 { "firstName": "John", "lastName": "Smith", "address": { "streetAddress": "2ndStreet", "city": "NewYork", "state": "NY" }, "esc\\aped": "\"hello\"" } firstName address address.city address.postal esc\aped
樣例輸出
STRING John OBJECT STRING NewYork NOTEXIST STRING "hello"
評測用例規模與約定
n ≤ 100,每行不超過 80 個字元。 m
≤ 100,每個查詢的長度不超過 80 個字元。 字串中的字元均為 ASCII 碼 33-126 的可列印字元,不會出現空格。所有字串都不是空串。 所有作為鍵的字串不會包含小數點 .。查詢時鍵的大小寫敏感。 50%的評測用例輸入的物件只有 1 層結構,80%的評測用例輸入的物件結構層數不超過 2 層。舉例來說,{"a": "b"} 是一層結構的物件,{"a": {"b": "c"}} 是二層結構的物件,以此類推。
試題分析:看到題目中字串包含著"{" ,"}",那麼我認為應該用棧的特徵,即見到“{”就入棧,遇到“}”就出棧。
題目中指出其他位置都可以插入一個或多個空格使得 JSON 的呈現更加美觀,也可以在一些地方換行,不會影響所表示的資料內容。 那麼既然這樣,乾脆用一個字串全部包含就可以了。(也就是n行的內容,用一個字串承接)
因為最後的輸出是一一對應的關係,所以利用map。
對於多種結構層數的問題,也利用棧的特性,在有“{”的情況下又來一個“{”,那麼就應該利用棧儲存住這個key(map中的key),因為往後要用到這個,在來“}”的情況,pop掉。這樣就解決了多層結構層數的問題。
再者就是字串的分析了,可以看出本題中只有一對正確的""才是起點和終點。因此需要專門的一個函式來分析字串。
#include <iostream>
#include<string>
#include<stack>
#include<map>
using namespace std;
/*
對於'{'的判斷,很明顯利用棧的性質來做.
字串是由雙引號 " 括起來的一組字元(可以為空)。
如果字串的內容中出現雙引號 ",在雙引號前面加反斜槓,也就是用 \" 表示;
如果出現反斜槓 \,則用兩個反斜槓 \\ 表示。反斜槓後面不能出現 " 和 \ 以外的字元。
*/
map<string, string>key;
map<string, string>::iterator it;
stack<string>linshi;
stack<string>qianzhui;
string c;
void change(string a, int num)//分析字串
{
string b;
for (int i = 0; i < a.length(); i++) {
if (a[i] == '\\')
{
if (i - 1 > -1 && a[i - 1] == '\\')
{
a[i] = 'a';
}
else if (i + 1 < a.length() && a[i + 1] == '\"')
{
}
else {
b += a[i];
}
}
else {
b += a[i];
}
}
if (num % 2 == 0)
{
key[linshi.top()] = b;
linshi.pop();
}
else
{
if (qianzhui.size() != 0)
{
b = qianzhui.top() + "." + b;
}
linshi.push(b);
}
}
map<string, string>ramdisk;
int main()
{
stack<char>bigkuo;
int n, m;
cin >> n >> m;
string *context = new string[n];
string *yu = new string[m];
string Queryfy, information;
cin.ignore();
for (int i = 0; i < n; i++)
{
getline(cin, context[i]);
information += context[i];
}
for (int i = 0; i < m; i++)
cin >> yu[i];
int qian, hou; bool you = true; int conut = 0;//通過這個count來確認到底這個是key還是value
for (int i = 0; i < information.length(); i++)
{
if (information[i] == '{') {
bigkuo.push('{');
if (bigkuo.size() > 1)
{
string u = "OBJECT0";
qianzhui.push(linshi.top());
change(u, conut += 1);
}
}
if (information[i] == '}') {
bigkuo.pop();
if (bigkuo.size() > 0) {
qianzhui.pop();
}
}
if (information[i] == '"'&&information[i - 1] != '\\')//第一種字串的終止情況判斷, 例如"iosa\"dsa"
{
if (you) {
qian = i;
you = false;
}
else {
hou = i; you = true;
change(information.substr(qian + 1, hou - qian - 1), conut += 1);
}
}
if (i-2>-1&&information[i] == '"'&&information[i - 1] == '\\'&&information[i-2]=='\\')//第二種字串的終止情況判斷,例如"\"\"\\\\"
{
hou = i;
you = true;
change(information.substr(qian + 1, hou - qian - 1), conut += 1);
}
}
for (int i = 0; i < m; i++)
{
it = key.find(yu[i]);
if (it != key.end())
{
if (it->second != "OBJECT0")
{
cout << "STRING" << " " << it->second << endl;
}
else {
cout << "OBJECT" << endl;
}
}
else
{
cout << "NOTEXIST" << endl;
}
}
}
明天就是CCF的考試了,不求盡如人意,但求問心無愧。