• 【ccf-csp题解】第11次csp认证-第三题-Json查询超详细讲解


    此题思路来源于acwing ccfcsp认证辅导课

    题目描述

     思路分析

    此题的难点在于对输入的内容进行解析,题目中说除了保证字符串内容不会有空格存在之外,其它的任意地方都可能出现空格,甚至在某些地方还会出现空行,这样的话,如果一行一行地处理数据,需要做出大量的分类讨论,所以在这里使用一个技巧,就是事先把所有数据拼成一行str:

    1. while(n--)
    2. {
    3. getline(cin,s);
    4. str+=s;
    5. }

    拼在一起称为一个str之后,可以设置一个游标k,表示现在已经处理到的str的下标

    接下来,分析对象的存储方式

    我们可以写下面这样一个结构体去存储:

    1. struct Obj
    2. {
    3. int type;//1:str,2:dict
    4. string str;
    5. Obj(){}
    6. Obj(string _str)
    7. {
    8. type=1;
    9. str=_str;
    10. }
    11. mapobj;
    12. }

    在这里面,type为1表示字符串,为2表示字典

    接下来开始对输入的对象进行解析:

    首先,可以写一个解析对象的函数Obj work_obj(string,int),这个函数传入待解析对象的字符串形式和处理的起始位置(本题中实际上就是传入0就行了,当然也可以传入上面说的游标k,毕竟刚开始这个k的值一定被初始化为0)在这个函数内部,首先创建一个Obj对象obj,这个就是最终解析之后的Obj用于后续的查询,把这个obj的type设置为2。由于前面有空格存在的可能性,所以用下面的代码过滤掉’{‘,使k进入obj字典的内部:

    1. while(str[k]!='{')k++;
    2. k++;

     然后使用下面的代码对后续的内容进行处理,只要不见到结束的标志符号’}’就一直处理下去:

    1. while(str[k]!='}')
    2. {
    3. if(str[k]=='\"')work_kv(obj,str,k);
    4. else k++;
    5. }

    在这个处理的过程当中,每遇到一个双引号,就代表即将要处理一个键值对,我们再写一个函数work_kv专门处理一个键值对,参数传入对应的Obj对象,str和游标k

    下面是这个work_kv函数具体实现

    1. void work_kv(Obj& obj,string &str,int &k)
    2. {
    3. auto key=work_str(str,k);
    4. while(str[k]!=':')k++;
    5. k++;
    6. while(str[k]!='\"'&&str[k]!='{')k++;
    7. if(str[k]=='\"') obj.obj[key]=Obj(work_str(str,k));
    8. else obj.obj[key]=work_obj(str,k);
    9. }

    首先,由于键一定是字符串,而字符串我们需要对'\'特殊处理,所以就专门地为字符串的处理写一个函数work_str,由于空格的存在,使用while循环找到':'的存在并过滤它,接下来就是对值的处理,值的开头就两种情况,如果是双引号开头就是string,如果是左大括号开头就是object。接下来对这两种情况分别讨论,如果是string,那么就直接创建一个新的OBJ对象赋给obj的字典中,如果不是string,直接调用已经写好的work_obj即可

    接下来是对work_str的实现

    1. string work_str(string &str,int &k)
    2. {
    3. k++;
    4. string res;
    5. while(str[k]!='\"')
    6. if(str[k]=='\\')res+=str[k+1],k+=2;
    7. else res+=str[k++];
    8. k++;
    9. return res;
    10. }

    这个函数传入的参数和work_obj相同,第一行会首先过滤掉字符串的开头符号——双引号,然后继续遍历,如果遇到了反斜杠就跳过即可,最后返回res

    以上就是解析过程的三个函数,在main函数中仅仅调用work_obj(str,0)即可,假设返回Obj类型的obj,下面是查询过程

    对于每一行的查询内容,先读入整行内容,然后以’.’为依据进行分隔,放入vector。由于分隔符’.’每次只会有一个,每次push_back之后,只需要让i等于j即可。对于有些以空格为依据进行分隔的情况,由于空格的数量是不确定的,所以具体要让i跳跃到哪里还需要一个新的while循环判断,当然还有一种简单的方法就是使用stringstream,链接如下:

    stringstream空格分割

    分成vector之后,需要遍历里面的每一个string,对于每个string,需要对上述返回的obj进行迭代(就是把obj的值变成obj中的字典key为string的值),最后根据具体情况给出查询结果,具体的就看最后的代码吧

    心得体会

    1.学会了把多行字符串拼接成一个字符串从而避免对各种各样的空行、空格分类讨论的思想

    2.学会了这种使用while循环去过滤某些字符的方法,而不是仅仅依靠string中的substr+find+replace函数去做,不仅费时间,而且代码难写

    3.学会了这种使用一个全局游标+工作函数对代码的结构化简化,使得整体的思路清晰,不容易混乱

    4.使用工作函数时,仅仅需要传入开始处理的位置即可,对于何时结束的问题交给工作函数本身处理就行

    完整代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. using namespace std;
    7. struct Obj
    8. {
    9. int type; // 1: str, 2: dict
    10. string str;
    11. Obj(){}
    12. Obj(string _str)
    13. {
    14. type = 1;
    15. str = _str;
    16. }
    17. map obj;
    18. };
    19. string work_str(string& str, int& k)
    20. {
    21. k ++ ; // 过滤掉'\"'
    22. string res;
    23. while (str[k] != '\"')
    24. if (str[k] == '\\') res += str[k + 1], k += 2;
    25. else res += str[k ++ ];
    26. k ++ ; // 过滤掉'\"'
    27. return res;
    28. }
    29. Obj work_obj(string&, int&);
    30. void work_kv(Obj& obj, string& str, int& k)
    31. {
    32. auto key = work_str(str, k);
    33. while (str[k] != ':') k ++ ;
    34. k ++ ; // 将':'过滤掉
    35. while (str[k] != '\"' && str[k] != '{') k ++ ;
    36. if (str[k] == '\"') obj.obj[key] = Obj(work_str(str, k));
    37. else obj.obj[key] = work_obj(str, k);
    38. }
    39. Obj work_obj(string& str, int& k)
    40. {
    41. Obj obj;
    42. obj.type = 2;
    43. while (str[k] != '{') k ++ ;
    44. k ++ ; // 过滤掉 '{'
    45. while (str[k] != '}')
    46. {
    47. if (str[k] == '\"') work_kv(obj, str, k);
    48. else k ++ ;
    49. }
    50. k ++ ; // 过滤掉 '}'
    51. return obj;
    52. }
    53. void query(Obj o, vector& qs)
    54. {
    55. for (auto& s: qs)
    56. {
    57. if (o.type == 1 || !o.obj.count(s))
    58. {
    59. puts("NOTEXIST");
    60. return;
    61. }
    62. auto t = o.obj[s];
    63. o = t;
    64. }
    65. if (o.type == 1) printf("STRING %s\n", o.str.c_str());
    66. else puts("OBJECT");
    67. }
    68. int main()
    69. {
    70. int n, m;
    71. cin >> n >> m;
    72. getchar();
    73. string str, s;
    74. while (n -- )
    75. {
    76. getline(cin, s);
    77. str += s;
    78. }
    79. int k = 0;
    80. auto obj = work_obj(str, k);
    81. while (m -- )
    82. {
    83. getline(cin, s);
    84. vector qs;
    85. for (int i = 0; i < s.size(); i ++ )
    86. {
    87. int j = i + 1;
    88. while (j < s.size() && s[j] != '.') j ++ ;
    89. qs.push_back(s.substr(i, j - i));
    90. i = j;
    91. }
    92. query(obj, qs);
    93. }
    94. return 0;
    95. }

  • 相关阅读:
    Android 10 中的隐私权变更
    Qt样式表应用
    某小厂java后端初面,记录一下
    PHPer 开始使用 Java
    Autowired和Resource的关系
    LeetCode知识点总结 - 114
    javascript--类型检测 type of 和 instanceof
    线程、线程组、线程池、锁、事务、分布式
    IB数学与音乐的融合
    基于SqlSugar的数据库访问处理的封装,支持多数据库并使之适应于实际业务开发中(2)
  • 原文地址:https://blog.csdn.net/m0_63222058/article/details/134403015