当前位置:首页 > 编程&AI > 正文内容

倒排索引建立搜索引擎的研究性学习

ryanlian2年前 (2024-06-30)编程&AI24578

摘要

全文搜索引擎主要包含下载、分析、索引、查询四大系统。在本次项目中,我们主要的研究对象是分析、索引、查询这三个系统,即对于已有信息进行分词处理并计入数据库,分析用户的查询请求后在数据库中检索并返回结果。在其实现过程中,倒排索引(Full Text Retrieval)是一种常用于全文搜索引擎中的索引表,它通过将文本分词并记录每个词汇的出现位置,用于高效地检索相关文档。在过去几十年里,倒排索引得到了广泛的研究与应用。

本研究以构建和优化全文搜索引擎为核心课题,针对中文文本信息检索的需求与挑战,采用多元化的研究方法和技术手段。本研究项目聚焦于构建一个全文搜索引擎,旨在通过整合先进的信息检索技术,提升中文文本处理的效率与精确度。项目初期,我们集中精力于分词算法的优化,使用C++实现了多元分词、词汇权重计算及停止词处理机制,显著增强了分词的准确性和实用性。在此基础上,我们进一步构建了倒排索引结构,通过高效的数据结构和算法设计,加快了信息检索速度并降低了资源消耗。项目后期,我们设计并实现了一款用户界面友好的搜索引擎前端,集成了关键词搜索、结果排序与分页浏览等功能,极大提升了用户体验。

关键词全文搜索引擎;分词;倒排索引;信息检索

微信图片_2026-03-26_180733_303.png

【图一:项目结构】

微信图片_2026-03-26_180804_301.png

【图二:索引代码片段】



1.问题提出背景

1.1问题来源

全文搜索引擎在日常生活中的地位已经十分重要,为用户提供了快速便捷的信息获取方式。而深入研究搜索引擎的核心技术和算法,可以对搜索结果的准确性和速度提升有着重要意义。自上世纪90年代起,随着互联网信息的爆炸式增长,如何提高搜索的效率和准确度成为了一大问题。源于对搜索引擎领域高效搜索算法的兴趣和对现有搜索引擎功能的探索,我们决定选择全文搜索引擎中的倒排索引作为研究课题。

1.2问题转化课题 

在阅读相关文献资料后,我们了解到全文搜索引擎主要包含下载、分析、索引、查询四大系统。通过与课题组成员的讨论,结合老师的指导,我们明确了在本次项目中,以分析、索引、查询这三个系统作为主要对象,以倒排索引作为课题的研究方向。最终,我们将问题转化为探究倒排索引并制作一个全文搜索引擎的具体课题。

1.3研究目的

本课题的主要目的是探索倒排索引在全文搜索引擎中的应用,研究设计一个全文搜索引擎并实现其基本功能。我们希望通过深入研究倒排索引,可以帮助我们探究搜索引擎相关的理论基础。

1.4 研究意义 

理论意义:倒排索引作为全文搜索引擎的核心技术,研究其原理和应用对于深入理解搜索引擎的工作原理和性能优化具有重要意义。

实践意义:该研究或制作的实践意义主要体现在以下几个方面:

- 提升搜索引擎的搜索准确性和速度,提供更好的搜索体验;

- 对于信息挖掘、大数据分析等领域具有重要应用价值。

2. 文献综述

2.1资料查阅概况

在本次研究中,资料查阅涵盖了多个学术资源库和平台,主要包括知网等知识服务平台。我们利用百度学术等通用搜索引擎检索相关研究文献和报告,还充分深入到GitHub等开源软件托管平台,查阅源代码、项目文档、开发日志以及用户讨论,从而追踪技术实践与应用案例。

 

2.2 文献综述

全文搜索引擎主要包含下载、分析、索引、查询四大系统。在本次项目中,我们主要的研究对象是分析、索引、查询这三个系统,即对于已有信息进行分词处理并计入数据库,分析用户的查询请求后在数据库中检索并返回结果。在其实现过程中,倒排索引(Full Text Retrieval)是一种常用于全文搜索引擎中的索引表,它通过将文本分词并记录每个词汇的出现位置,用于高效地检索相关文档。在过去几十年里,倒排索引得到了广泛的研究与应用。

 

在国内外研究中,早期的倒排索引研究主要集中在信息检索领域,如效率优化、压缩技术、查询处理等方面。随着互联网的快速发展和大规模文本数据的产生,倒排索引及其相关技术也逐渐被应用于互联网搜索引擎中,如Google、Bing等。

 

倒排索引在信息检索领域具有重要的研究和应用价值。国内外学术界对其进行了广泛的研究,取得了许多有益的成果。我们希望可以探究倒排索引并进一步优化算法效率、提高搜索结果的质量和精确度。

2.2.1研究、调查(或制作)的理论或原理依据

倒排索引是一种常用的搜索引擎索引算法,其基本原理是通过构建文档-词项的倒排索引表,实现快速匹配和检索。通过将文档中的词项进行分词处理,并建立倒排索引表,将词项和其出现的文档位置进行记录,从而使得搜索引擎能够根据用户查询的词项快速定位到相关的文档。

2.2.2研究、调查或制作方法

在分词方面,除了使用常规词典分词外,利用基于前缀词典实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图。采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合。对于未登录词,采用了基于汉字成词能力的 HMM 模型,使用了 Viterbi 算法。

在倒排索引构建环节,我们设计了一套高效的数据结构与算法,以文档ID为键,每个键下存储该词出现的所有文档位置,极大地优化了查询速度。通过对海量文档的预处理,建立快速检索索引,使得用户查询时能迅速定位到相关文档,提升了用户体验。

此外,我们还构建了图形用户界面,集成了关键词输入、高级搜索选项、搜索结果显示及排序等功能模块。

2.2.3 研究、调查结论或制作结果

全文搜索引擎的成功构建在于其经过分词后构建的高效的索引结构设计与精确的信息检索算法结合,从而确保了用户在短时间内获取到大量相关且高质量的信息资源。他们的研究提供了一套成熟且可行的设计与实现方案,也深化了我们对全文搜索引擎工作原理的理解。

2.2.4 综合评述

对于全文搜索引擎的研究,现有文献和实践在基础理论、方法运用及成果展现等方面已经形成了丰富且扎实的基础。研究立足于经典信息检索原理,巧妙融合关键词匹配与统计分析等核心技术,成功构建了一套高效且广泛应用的搜索架构。然而,在肯定这些核心算法价值的同时,也应关注到该领域尚待挖掘和完善的空间。我们期望通过对基础算法的更深入探究,解决用户在日常使用中遇到的部分实际问题,从而推动全文搜索引擎功能的持续优化与用户体验的不断提升。

3. 研究或调查的理论基础(或制作原理)

倒排索引是一种常用的搜索引擎索引算法,其基本原理是通过构建文档-词项的倒排索引表,实现快速匹配和检索。通过将文档中的词项进行分词处理,并建立倒排索引表,将词项和其出现的文档位置进行记录,从而使得搜索引擎能够根据用户查询的词项快速定位到相关的文档。

4.核心概念界定

- 全文搜索引擎(Full Text Retrieval):通过文字为主的信息建立的数据库中,检索与用户查询条件匹配的相关记录,然后按一定的排列顺序将结果返回给用户。主要包含:下载系统,用于从Web上采集各种类型的网页信息,并保持对Web变化的同步;分析系统,用于对下载系统采集的信息进行PageRank和分词计算;索引系统,用于将分析系统处理后的网页对象索引入库;查询系统,用于分析用户提交的查询请求,然后从索引库中检索出相关网页并将网页排序后,以查询结果的形式返回给用户;

- 倒排索引:一种通过词项和其对应的文档位置进行倒排记录的索引表。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址;

- 分词:将长文本进行分割处理,提取出有意义的词汇。

5. 研究方法及过程 

5.1 研究成员分工

- 本人:负责倒排索引的实现和优化,进行实验测试和性能分析。

- 同学A:负责文献资料的搜集和整理,进行实验设计和数据分析。

- 同学B:写心得感受

 

5.2 研究或调查对象

本研究以国内外主流的全文搜索引擎为核心研究对象,包括百度、Bing等具有广泛用户基础和技术代表性的搜索引擎。以及著名开源分词库Jieba、IK等,和著名开源全文搜索引擎工具包Lucene。

5.3 研究方法

文献法:通过查阅国内外相关文献资料,系统梳理全文搜索引擎的发展历程、关键技术及最新研究成果,了解并总结已有的信息检索理论、关键词匹配算法以及统计分析模型等在全文搜索中的应用情况。

技术解析法:通过对著名开源分词库Jieba技术原理进行详细剖析,理解分词机制、处理流程以及相关优化策略,为后续的改进措施提供理论依据。

5.4 研究条件或设置说明

本研究将在遵守数据隐私保护的相关法律法规下进行。在本地使用少量文本数据进行数据处理。

5.5 结题阶段已完成的研究   

在C++上对Jieba进行了简单的复现。

      在Java上复现Lucene的索引实现并调用Jieba、IKanalyzer等不同分词器实现完整搜索引擎构建。

      使用JavaScript构建了搜索引擎图形界面。

6. 结题阶段的实验(或调查)结果分析与讨论

6.1目前得到的实验结果(调查)结果

在C++上复现Jieba基本功能的分词结果:我们成功复现了Jieba分词器的基本功能,实现了高性能的中文文本分词。复现的分词系统在准确率和效率上达到了较高水平。

       在Java上利用Lucene接口,并复现了Lucene的索引实现,然后调用Jieba、IKanalyzer等不同分词器实现完整搜索引擎构建的结果:Jieba分词器在中文文本处理上展现了良好的语义理解能力,特别是在长句和复杂语境下的分词准确度高,有利于提高搜索结果的相关性。IKanalyzer在处理速度上表现出色,特别是在处理大体积文本时,其分词速度较Jieba快,适合需要快速响应的应用场景。

 

6.2目前得到的结果分析与讨论

在Java上调用Jieba模块实现的分词结果中,使用了多元分词且颗粒度较细,这种较详细的分词结果比较适宜搜索引擎的使用环境。分词的基本算法有正向最大匹配算法、逆向最大匹配算法、双向最大匹配算法,其中分词结果的质量三者依次递增,但双向最大匹配算法的时间复杂度高于其他两者。多元分词可显著提高分词质量,但也会增高错误分词的概率。为降低错误分词概率,可加入词汇权重算法。通过在Java平台上使用Lucene框架与不同分词器的集成测试,Jieba因其高准确度更适合于需要高度精确搜索结果的场景,而IKanalyzer则在处理速度和灵活性方面展现出优势。

 

7.结题阶段性研究总结   

7.1结题阶段性研究结论

在中文文本分词、倒排索引的构建、搜索引擎图形界面的研究中,我们取得了以下实质性突破:

多元分词算法实现:从单一的一元切分模式扩展至多元分词方案,能够有效识别并正确划分出具有多个可能切分方式的词语,从而提高分词的全面性和准确性。

词汇权重算法嵌入:在原有词典基础上,我们进一步优化了分词效果,设计并实现了简易的词汇权重算法,使得高频且语义重要的词汇在分词过程中获得更高的优先级,有利于后续信息检索时提升相关性排序质量。

停止字典处理机制:我们在研究Jieba模块的过程中并未发现它具有此功能,我们借鉴IK模块中成熟的停止词典功能,对无关字符进行有效剔除,仅保留对搜索意义重大的词汇,并以此为依据初步分词,以此减轻系统负担并提高搜索效率。

索引构建流程:我们首先遍历经过分词处理后的文档集,为每个唯一词汇生成一个列表,该列表记录了所有包含该词汇的文档ID及其在文档中的位置。这一过程涉及到了深度的数据结构设计与优化,确保索引既能快速插入又能高效查询。

交互功能实现:用户通过输入框输入关键词后,界面即时触发搜索请求,后台处理后通过倒排索引迅速返回搜索结果,并在界面上动态展示。我们还实现了结果分页、关键词高亮显示等功能,增强了信息的可读性和查找效率。

      

       我们最终了解并实现了搜索引擎的基本构建,并可通过多元分词、词汇权重、停止字典、倒排索引、图形界面进而推动整体搜索引擎性能的显著提升和使用便捷性的进步,满足信息检索需求。

7.2后续还要继续进行的研究

语义理解与查询扩展:当前搜索引擎主要基于关键词匹配,未来我们将探索如何利用深度学习等先进技术增强语义理解能力,实现对用户查询意图的精准捕获。这包括但不限于查询改写、同义词识别、上下文理解等,以提供更加贴近用户需求的搜索结果。

     跨媒体检索:随着多媒体内容的增加,发展能够处理文本、图像、语音等多种类型数据的统一搜索框架变得尤为重要。探索多模态融合技术,实现基于内容的多媒体检索,可以极大地拓宽搜索引擎的应用场景。

8. 参考文献

-      张卫丰、徐宝文、周晓宇,Web搜索引擎综述,https://wap.cnki.net/touch/web/Journal/Article/JSJA200109006.html

-      都云程、卢献华,中文搜索引擎现状与展望;

-      印鉴、陈忆群、张钢,搜索引擎技术研究与发展;

-      张骞,传统搜索引擎与智能搜索引擎比较研究;

-      张俊飞,全文搜索引擎搭建解析,https://kns.cnki.net/kcms2/article/abstract?v=IUBLoWpfHZF2t2axQ73Ps197mmGue0kwzfkDX414ZpnNR10j8m5TtsSENpzjxn7oTHtcmnL9DLvkB-NGll8dBmViTawNU66HH7lEmtfUl-S_E4ZW1ItRsiyhmPx5AqigKUsoRODXR6s=&uniplatform=NZKPT&language=CHS

-      胡鹏飞,Lucene与中文分词技术的研究及应用,https://kns.cnki.net/kcms2/article/abstract?v=IUBLoWpfHZG2xTk2CbLO-8bzAtqevZfsyi6B5NOiHGjA24eFDq_kMIr-QmhmGaKNoLQ_wGM4hUwUaXZr5LFa75s6iC2ywLh-kDDQI92ZlrXCsq_SUqpQJp5jP_EBMd4FXn1PbPkbr2oWMlpHUEZ1yQ==&uniplatform=NZKPT&language=CHS

-      苏潭英、郭宪勇、金鑫,一种基于Lucene的中文全文检索系统,https://wap.cnki.net/touch/web/Journal/Article/JSJC200723031.html

-      郑榕增、林世平,基于Lucene的中文倒排索引技术的研究,https://wap.cnki.net/touch/web/Journal/Article/WJFZ201003022.html

-      张校乾,基于Lucene的全文检索系统的研究与应用,https://wap.cnki.net/touch/web/Dissertation/Article/10141-2005051397.nh.html

-      邓攀、刘功申,一种高效的倒排索引存储结构,https://wap.cnki.net/touch/web/Journal/Article/JSGG200831043.html

-      刘兴宇,基于倒排索引的全文检索技术研究,https://wap.cnki.net/touch/web/Dissertation/Article/10487-2005038406.nh.html

-      王林,搜索引擎的原理和发展;

-      李建平、简晨,浅析全文搜索引擎solr;

-      崔飞虎、潘正运,基于互联网的全文搜索引擎模型;

-      颜素莉,主流中俄文搜索引擎核心技术分析与比较研究;

-      吴文娟、车明,搜索引擎倒排索引技术的改进,https://wap.cnki.net/touch/web/Journal/Article/WCLJ200606026.html

-      曲卫华、王群,搜索引擎原理介绍与分析开发研究与设计技术;

-      黄立冬,分布式搜索引擎中关键词倒排索引方法仿真,https://wap.cnki.net/touch/web/Journal/Article/JSJZ201908079.html

-      全文搜索引擎, https://baike.baidu.com/item/%E5%85%A8%E6%96%87%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/7847410;

-      百度百科:排索引, https://baike.baidu.com/item/%E5%80%92%E6%8E%92%E7%B4%A2%E5%BC%95?fromModule=lemma_search-box

9. 附录  

附1

Jieba库的使用手册及代码示例

·  jieba.cut 方法接受四个输入参数: 需要分词的字符串;cut_all 参数用来控制是否采用全模式;HMM 参数用来控制是否使用 HMM 模型;use_paddle 参数用来控制是否使用paddle模式下的分词模式,paddle模式采用延迟加载方式,通过enable_paddle接口安装paddlepaddle-tiny,并且import相关代码;

·  jieba.cut_for_search 方法接受两个参数:需要分词的字符串;是否使用 HMM 模型。该方法适合用于搜索引擎构建倒排索引的分词,粒度比较细

· 待分词的字符串可以是 unicode UTF-8 字符串、GBK 字符串。注意:不建议直接输入 GBK 字符串,可能无法预料地错误解码成 UTF-8

·  jieba.cut 以及 jieba.cut_for_search 返回的结构都是一个可迭代的 generator,可以使用 for 循环来获得分词后得到的每一个词语(unicode),或者用

·  jieba.lcut 以及 jieba.lcut_for_search 直接返回 list

·  jieba.Tokenizer(dictionary=DEFAULT_DICT) 新建自定义分词器,可用于同时使用不同词典。jieba.dt 为默认分词器,所有全局分词相关函数都是该分词器的映射。

代码示例

# encoding=utf-8
import jieba
 
jieba.enable_paddle()# 启动paddle模式。 0.40版之后开始支持,早期版本不支持
strs=["我来到北京清华大学","乒乓球拍卖完了","中国科学技术大学"]
for str in strs:
    seg_list = jieba.cut(str,use_paddle=True) # 使用paddle模式
    print("Paddle Mode: " + '/'.join(list(seg_list)))
 
seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式
 
seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精确模式
 
seg_list = jieba.cut("他来到了网易杭研大厦")  # 默认是精确模式
print(", ".join(seg_list))
 
seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式
print(", ".join(seg_list))


 

 

附2

对Jieba库复现程序的源代码及分词效果

/* !!!一个汉字在字符串中占两个位 !!! */
#include <iostream>
#include <string>
#include <fstream> // 文件读取
#include <ctime>
using namespace std;
 
ifstream inFile; // 输入对象
ofstream outFile; // 输出对象
bool shortEnding = false; // 本短句子是否完成
int cutHow = 2; //  短句子完成后剪掉几个
bool longEnding = false; // 本句子是否完成
 
 
struct Words{
     string word;
     int weight;
};
 
Words getWord(bool news){ // 是否获取新词
     // 获取词典中的词
     if(news){ // 从头开始
              inFile.close(); // 关闭并重开
              inFile.open("dictionary.txt");
     }
     Words nowWord;
     inFile >> nowWord.word >> nowWord.weight;
     return nowWord;
}
 
string cutHead(string shortSentence){
     // 剪去第一个字符
     return shortSentence.substr(2, shortSentence.size() - 2);
}
 
int same(string shortSentence, Words nowWord){ // 当前短句子(被剪的句子),词
     // 主判断函数
     /*
     若句被剪完,返回
     若词典遍历完,换 下一个句子 和 从头开始的词
     若匹配失败,换 本句子 和 下一个词
     若匹配成功,换 下一个句子 和 头开始的词
     */
     if(shortSentence.size() == 2){ // 当前句子剩一个字
              outFile << shortSentence << endl;
              shortEnding = true;
              return cutHow; // 剪掉最后一个字
     }
     if(shortEnding){// 一直退回
              return cutHow;
     }
     
     if(nowWord.word == "endingWordForTheDictionary"){ // 字典遍历完
              Words nextWord = getWord(1);
              same(cutHead(shortSentence), nextWord); // 句next,词从头开始
     }
     if(shortSentence != nowWord.word){
              Words nextWord = getWord(0);
              same(shortSentence, nextWord); // 句不变,词next
     }
     if(shortSentence == nowWord.word){ // 匹配成功
              outFile << shortSentence << endl; // 有意义词汇
              
              Words nextWord = getWord(1);
              if(shortSentence.size() == 4){ // 当前句子剩两个字
                      shortEnding = true; //不再细分
                      cutHow = 4;
                      return cutHow; // 剪掉这个词
              }
              if(nowWord.weight > 0){ // 词汇权重大
                      shortEnding = true; //不再细分
                      cutHow = nowWord.word.size();
                      return cutHow; // 剪掉这个词
              }
              else{
                      same(cutHead(shortSentence), nextWord); // 句next,词从头开始
              }
     }
     else{
              return cutHow;
     }
}
 
string cutTail(string longSentence, int cut){
     // 减去最后一个字符
     return longSentence.substr(0, longSentence.size() - cut);
}
 
void parater(string longSentence){
     // 对一个句子分词
     if(longSentence.size() == 0){ // 当前句子被分完(从尾)
              longEnding = true;
              return;
     }
     if(longEnding){ // 一直退回
              return;
     }
     shortEnding = false; // 重置短句子状态
     cutHow = 2; // 重置短句子状态
     Words nextWord = getWord(1);
     int cut = same(longSentence, nextWord); // 完成对切(从尾)n次句子的分词
     parater(cutTail(longSentence, cut)); // 切对(从尾)n+1次句子的分词
}
 
int main(){
     string userin; // 待分词文本导入
     string dict; // 字典中的某个词
     cin >> userin;
     userin += "。"; // 最后必须以标点符号结尾
     inFile.open("dictionary.txt"); // 字典导入,编码:ANSI
     outFile.open("usersOut.txt"); //  结果导出
     
     cout << "字数:" << userin.size() / 2 << endl;
     long start, end;
     start=clock(); //程序开始计时
     
     // 初步分句子
     string firCut;
     firCut.clear();
     do{
              string firLetter = userin.substr(0, 2); // 获取userin的首字,汉字占两个位置
              userin.erase(0, 2); // 去掉首字
              
              if(firLetter != "," && firLetter != "。" && firLetter != "的" && firLetter != "了" && firLetter != "、" && firLetter != "“" && firLetter != "”"){
                      firCut += firLetter;
              }
              else{
                      longEnding = false; // 重置长句子状态
                      parater(firCut); // 对初步分好的句子分词
                      firCut.clear();
              }
     }while(userin.empty() == false);
     
     end=clock(); //程序结束用时, 以ms为单位
     cout << "用时:" << end - start << "ms" << endl; //ms为单位
     inFile.close(); // 关闭文件
     outFile.close();
     return 0;
}
 
分词原文:“一二·九”运动是中国近代史上的一次重要历史事件,它标志着中国青年在民族危亡之际的觉醒和奋起,又值国家公祭日,在这个特殊的时刻,我们举行了这次活动,旨在铭记历史、缅怀先烈、珍爱和平,弘扬爱国主义精神,激发广大师生的爱国热情和奋斗精神。
分词结果:九  ·  一二  上  近代史  史  近代  中国  是  运动  事件  历史  重要  一次  危亡之际  之际  危亡  民族  在  青年  中国  着  标志  它  奋起  和  觉醒  日  公祭  国家  又值  特殊  这个  在  时刻  举行  我们  活动  这次  历史  铭记  旨在  先烈  缅怀  和平  珍爱  精神  爱国主义  主义  爱国  弘扬  师生  广大  激发  精神  奋斗  和  爱国热情  热情  爱国


相关文章

高考数据查询系统开发手记

高考数据查询系统开发手记

一、起手:5万行数据引发的开发契机高考刚结束,爸爸递给我一份Excel文件,里面存放着5万多行北京高考历年录取数据。平日里他习惯用Excel筛选查询院校、专业与录取分数线,但整套操作流程十分繁琐。“爸...

评论列表

骑猪看日出
骑猪看日出
2年前 (2024-09-13)

我也是中学生,正在做类似的研究性学习,能不能请教一下索引创建的细节?

ryanlian 回复:
当然可以!可以私我
2年前 (2024-09-20)
李老师
李老师
2年前 (2024-08-20)

能将理论知识转化为实践,动手能力极强,这份研究报告非常优秀

后端老周
后端老周
2年前 (2024-08-26)

作为从业5年的后端工程师,必须夸一句:后生可畏,思路和实现都很成熟

雾里敲码
雾里敲码
2年前 (2024-08-20)

研究性学习能做到这个程度,真的太厉害了!求后续分享更多实现细节

代码逐光者
代码逐光者
2年前 (2024-08-01)

太牛了!中学生能自己动手实现倒排索引搜索引擎,思路清晰,代码也很规范

ryanlian 回复:
哈哈,折腾了快一个月才跑通所有功能,后续会分享更多实现细节,谢谢支持!
2年前 (2024-08-10)
参与者
参与者
2年前 (2024-06-30)

老大辛苦了,我也躺赢了一个校级优秀????

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。