GitHub链接 https://github.com/huaku-i/031702147.git
一、前言
就是后悔,非常后悔,真的不懂为啥自己钢铁侠选了C++来写,可能作死吧
但是估计很多人都是用C++写的,想了想我好你好大家好,写个大家都看得懂的版本……QUQ
(这个C++版本只能实现非附加题的问题 看了百度API自动补全 觉得用PY比较好写,
这周末看看用PY 弄一个V2.0版本解决附加题应该会好点
PS:如果评测结果不对 那可能和评测工具不兼容吧
二、个人开发流程(PSP表格)
PSP2.1 | Personal Software Process Stages | 预估耗时(min) | 实际耗时(min) |
---|---|---|---|
* Planning | 计划 | 10 | 25 |
Estimate | 估计这个任务需要多少时间 | 10 | 25 |
* Development | 开发 | 500 | 685 |
Analysis | 需求分析 (包括学习新技术) | 180 | 120 |
Design Spec | 生成设计文档 | 15 | 20 |
Design Review | 设计复审 | 10 | 30 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 15 | 45 |
Design | 具体设计 | 20 | 20 |
Coding | 具体编码 | 200 | 240 |
Code Review | 代码复审 | 60 | 120 |
Test | 测试(自我测试,修改代码,提交修改) | 60 | 90 |
Reporting | 报告 | 60 | 80 |
Test Repor | 测试报告 | 30 | 30 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 20 | 40 |
* total | 合计 | 570 | 830 |
三、设计与实现
(1)分析与假设(通过询问,可能这就是面向数据编程我得到了一下几条有用的假设与信息
- 输入输出都是 UTF8字符编码格式
- 对于非附加题,第1级,省级行政区:在“XX省”这样的省后缀结尾可能省略“省”,不可能出现缺失
- 对于非附加题,第2级,地级行政区:在“XX市这样的市后缀结尾”这样的可能省略“市”,同时第二级可能出现缺失
- 对于非附加题,第3级-第7级地址都可能出现缺失,但不可能出现像前2级那样的省略了(也就是说,只可能省略“省”和“市”这两个字)
- 假设输入一定是:X!姓名,+地址+电话给出,电话暂定为11位(其实别的位只要连续,也很容易判断)
- Last but not least 如果我的询问和假设和最终样例出现了偏差…… 那我也没办法QUQ(估计评测当场裂开
(2)需求与设计
PS:因为不知道评测提交需要的要求和文件,怕出问题先写到一个CPP里头了 处理的对象:
名称 | 说明 | 备注 |
---|---|---|
id | 地址id | 直辖市1,自治区2,普通省份3 |
difficult | 难度 | 分为1!、2!、3! |
str | 主字符串 | |
name | 名字 | 以逗号分隔 |
phone | 电话 | 11位号码 |
address1 | 第1级地址 | 23个省、5个自治区、4个直辖市、2个特别行政区 |
address2 | 第2级地址 | 293个地级市、7个地区、30个自治州、3个盟 |
address3 | 第3级地址 | 963个市辖区、382个县级市、1329个县、117个自治县、49个旗、3个自治旗、1个林区、1个特区 |
address4 | 第4级地址 | 包括8393个街道、21297个镇、9120个乡、981个民族乡、152个苏木、1个民族苏木、1个县辖区 |
address5 | 第5级地址 | 路、巷、弄等 |
address6 | 第6级地址 | 号 |
address7 | 第7级地址 | 其他 |
other | 附加 |
- 核心函数 :Difficult + Solve +碎碎念
(1)UFT8处理 按顺序来,首先当然是那个UTF8的处理QUQ,这里首先要感谢东哥 一开始没考虑那么多 我用C++写到一半
忽然发现要处理中文字符非常麻烦,本来想直接转PY JAVA了后来东哥抬了我一手 让我知道怎么处理UTF8和Unicode转换
点此查看详细代码 +
//UTF8对应的字符串类型是wstring,其余操作和string一样
//一开始读进来的时候记得用string读入,然后转成UTF8 wstring 处理,最后再转回string输出
string UnicodeToUTF8(const wstring&s) {
string ret;
wstring_convert wcv;
ret = wcv.to_bytes(s);
return ret;
}
wstring UTF8ToUnicode(const string &s) {
wstring ret;
wstring_convert wcv;
ret = wcv.from_bytes(s);
return ret;
}
text.address1 = L"";//wstring定义时候要加一个L,其他同理
(2)其他string处理函数
点此查看详细代码 +
size_type find( const basic_string &str, size_type index ); //返回str在字符串中第一次出现的位置(从index开始查找),如果没找到则返回string::npos
string &insert(int p,const string &s); //在p位置插入字符串s
string &replace(int p, int n,const char s); //删除从p开始的n个字符,然后在p处插入串s
string &erase(int p, int n); //删除p开始的n个字符,返回修改后的字符串
string substr(int pos = 0,int n = npos) const; //返回pos开始的n个字符组成的字符串
void swap(string &s2); //交换当前字符串与s2的值
string &append(const char s); //把字符串s连接到当前字符串结尾
void push_back(char c) //当前字符串尾部加一个字符c
const char data()const; //返回一个非null终止的c字符数组,data():与c_str()类似,用于string转const char其中它返回的数组是不以空字符终止,
const char c_str()const; //返回一个以null终止的c字符串,即c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同,用于string转const char
(3)部分匹配函数代码
第1级:根据汉字匹配
点此查看详细代码 +
int s_address1() {//第一级处理,省级行政区,23个省、5个自治区、4个直辖市
//直辖市1、自治区2、普通省份3(普通省份多特判一个黑龙江)
//判断直辖市
for (int i = 0; i < 4; i++) {
wstring temp = L"";
temp += text.str[0];
temp += text.str[1];
if (temp == special[i]) {
text.address1 = text.str.substr(0, 2);
text.str.erase(0, 2);
if (text.str[0] == L'市')
text.str.erase(0, 1);
return 1;
}
}
//判断自治区
int k = text.str.find(L"自治区");
if (k != text.str.npos) {
text.address1 = text.str.substr(0, k + 2);
text.str.erase(0, k + 2);
return 2;
}
//判断普通省份
if (text.str[0] == L'黑') {//特判黑龙江,因为在普通省份中特字数4个字
if (text.str[3] != L'省') {
text.str.insert(3, L"省");
}
text.address1 = text.str.substr(0, 4);
text.str.erase(0, 4);
return 3;
}
else {
if (text.str[2] != L'省') {
text.str.insert(2, L"省");
}
for (int i = 0; i <= 2; i++)
text.address1 += text.str[i];
text.str.erase(0, 3);
return 3;
}
return 0;//第一级不可能缺失,除了附加题
}
第2级直接本地打表,暴力匹配+补全
点此查看详细代码 +
void s_address2() {//第二级处理,293个地级市、7个地区、30个自治州、3个盟,(有时候会出现缺失,因此需要匹配操作)
//市、自治州
if (text.id == 1) {//直辖市特判
text.address2 = text.address1 + L"市";
return;
}
else{//其他省份int k = text.str.find(L"盟"); if (k != text.str.npos) { text.address2 = text.str.substr(0, k + 1); text.str.erase(0, k + 1); return; } k = text.str.find(L"自治州"); if (k != text.str.npos) { text.address2 = text.str.substr(0, k + 3); text.str.erase(0, k + 3); return; } for (int i = 0; i <= 332; i++) {//地级市(可能会省略“市”,所以要补齐操作)//自治州、盟、地区、 if (text.str.find(city[i]) ==0) { int k = city[i].length(); text.str.insert(k, L"市"); text.address2 = text.str.substr(0, k + 1); text.str.erase(0, k + 1); if (text.str[0] == L'市') text.str.erase(0, 1); return; } } } text.address2 = L"";//全部都没找到就为第二级全部缺失 return;
}
第3级-第7级暴力识别匹配
(4)主解决函数
点此查看详细代码 +
void solve(string s) {text.str = UTF8ToUnicode(s);//转码 if (text.str[0] == L'0')text.difficult = 0; if (text.str[0] == L'1')text.difficult = 1; if (text.str[0] == L'2')text.difficult = 2; text.str.erase(0, 2); int len = text.str.length(); text.str.erase(len - 1, 1);//处理前缀后缀英文句点,难度信息 s_phone(); s_name(); text.id = s_address1(); s_address2(); s_address3(); s_address4(); s_address5(); s_address6(); s_address7();
}
说实话V1.0有点暴力 ……其余就不一一列出了,核心思想就是匹配查找+字符串切割、更新
四、性能改进
查找对比了一下突然发现主要耗时在转码和读入输出上OTL ,当然匹配也花费了一些时间OTL
重新修改了一下 匹配用了KMP(虽然查找匹配量不大的话可能不太明显?)
同时优化了一下异常处理和输出JSON函数(GAN 最后C++还是得手敲轮子)QUQ
五、单元测试
构造了4个函数address_plus1()、address_plus2()、address_phone()、address_name()进行单元测试比对
同时也想了特殊EXAMPLE:
更过分的是……
凶残的不讲道理
询问福哥正确性+自己查了一下后
得出
- 一级行政区省级行政区34个:23个省、5个自治区、4个直辖市、2个特别行政区;
- 二级行政区地级行政区333个:293个地级市、7个地区、30个自治州、3个盟。
- 三级行政区县级行政区2845个:963个市辖区、382个县级市、1329个县、117个自治县、49个旗、3个自治旗、1个林区、1个特区。(截止2019年9月)
- 四级行政区乡级行政区39945个:包括8393个街道、21297个镇、9120个乡、981个民族乡、152个苏木、1个民族苏木、1个县辖区。(截止2018年底)
同时构造了如下数据检测覆盖率,进行单元测试
部分特殊input:
2!张三,湖北省随州随县吴山镇唐王街联宏村18883549874委会.
2!李四,福建省福州13756899511市鼓楼区鼓西街道湖滨路110号湖滨大厦一层.
1!王五,福建福州闽13599622362侯县上街镇福州大学10#111.
2!红太狼,福建省福州市鼓楼18960221533区五一北路123号福州鼓楼医院.
1!灰太狼,广东省东莞市凤岗13965231525镇凤平路13号.
2!喜羊羊,云南省湘西土家族苗族自治州13965231525香格里拉市福州路10#112.
1!小张,福建省福州市福清市龙江18150632336街道福山路43号中银公寓.
2!小明,福18150632336建福州福清福山路43号.
1!小洪,陕西省铜川18150632336福州路22号.
2!小王,陕西铜川18150632336.
2!小郑,福建.
相对output:
[{"地址":["湖北省","随州市","随县","吴山镇","唐王街","","联宏村委会"],"姓名":"张三","手机":"18883549874"},
{"地址":["福建省","福州市","鼓楼区","鼓西街道","湖滨路","110号","湖滨大厦一层"],"姓名":"李四","手机":"13756899511"},
{"地址":["福建省","福州市","闽侯县","上街镇","福州大学10#111"],"姓名":"王五","手机":"13599622362"},
{"地址":["福建省","福州市","鼓楼区","","五一北路","123号","福州鼓楼医院"],"姓名":"红太狼","手机":"18960221533"},
{"地址":["广东省","东莞市","","凤岗镇","凤平路13号"],"姓名":"灰太狼","手机":"13965231525"},
{"地址":["云南省","湘西土家族苗族自治州","香格里拉市","","福州路","","10#112"],"姓名":"喜羊羊","手机":"13965231525"},
{"地址":["福建省","福州市","福清市","龙江街道","福山路43号中银公寓"],"姓名":"小张","手机":"18150632336"},
{"地址":["福建省","福州市","","","福清福山路","43号",""],"姓名":"小明","手机":"18150632336"},
{"地址":["陕西省","铜川市","","","福州路22号"],"姓名":"小洪","手机":"18150632336"},
{"地址":["陕西省","铜川市","","","","",""],"姓名":"小王","手机":"18150632336"},
{"地址":["福建省","","","","","",""],"姓名":"小郑","手机":""}]
六、异常处理
- 处理对输入为空时的警告输出
if (text.str == L"")
fout << "input error!" << endl;
- 处理了输入不为UTF8时的警告输出
if (text.str[0] != L"1"||text.str[0] != L"2"||text.str[0] != L"3")
fout << "string error!" << endl;
- 同时也增加了对输入警告错误输出
if (text.name == L"")
fout << "name error!" << endl;
if (text.phone == L"")
fout << "phone error!" << endl;
对于匹配不到11位(暂定)的电话号码和识别不到姓名的异常情况会输出错误
七、ENDING(碎碎念)
- 一开始是想到用PY写的……但是题目不让我用、
开始写了之后吧……… 又改题目让用了 - 输入输出格式一开始没那么多规定……写着写着,变UTF8了……
(虽然说不改中文字符也过不去)本来想GG半路上华一大佬的py贼船了,又狠不下写了一半的代码 - 问题解决了、、写的差不多了……才发现一些神奇的地区和设定问题,于是又自闭踏上了DEBUG和优化的漫漫路
- 都做完了 才发现这评测工具无教程只有命令行
(甚至像病毒)才是最难的OTL甚至想锤ymz,C++手造轮子 基本没队友+没人权=自闭…… - 其实也是可以用正则匹配的,不过想了想反正都要打表匹配补全,还不如直接全暴力处理了主要函数都类似改起来也快0.0
又碰上事情多的一周 当场裂开(满脸心酸.jpg 被自己菜哭)