[C#] 逆袭——自制日刷千题的AC自动机攻克HDU OJ
前言
做过杭电、浙大或是北大等ACM题库的人一定对“刷题”不陌生,以杭电OJ为例:首先打开首页(http://acm.hdu.edu.cn/),然后登陆,接着找到“Online Exercise”下的“Problem Archive”,然后从众多题目中选择一个进行读题、构思、编程、然后提交、最后查看题解状态,如果AC了表示这一题被攻克了,否则就要重做了~一般情况下,“刷题”要求精神高度集中且经验丰富,否则很难成功AC,有时候甚至做一题要浪费半天的时间!(有时网速卡了,比抢火车票还要急!)
楼主在这里先给广大辛勤“刷题”的ACMer赔个不是,因为本文所介绍的AC自动机其实是利用爬虫从网上搜索题目答案,然后再利用C#的web控件和鼠标、键盘事件来自动提交题目的投机式机器人(纯属楼主自娱自乐,多多见谅!)。
注:下图依次是①主页面;②题目列表页面;③题目页面;④提交代码页面;⑤提交结果查看页面
成果
参看杭电OJ的RankList(http://acm.hdu.edu.cn/ranklist.php),目前我用这个AC自动机粗略的刷一遍整个题库共提交12391次,解决2688个题目,正确率21.69%,总排名第8。同时我还发现至少2个和我属于同一类的考机器人刷题的“捣蛋鬼”,其中一个是排名第2到第7的三国蜀国的将领们,另一个是几乎占据17~28名的hdujudge0~n。为什么能发现他们?哈哈,①没人会连续几天不停的刷题的;②总提交数高的离谱;③正确率低的吓人;④我在浙大OJ上也遇到了他们(哈哈哈)。在此,我想邀请二位合伙做一个自动爬题+自动分析代码的题库解析的网站,也算是我们利用自己捣蛋的玩具做的一点好事~嘿嘿~(此外,我非常佩服排名第一的那位,如果是人刷的其毅力和能力绝对一流;如果是机器刷的,能达到53.80%的正确率也是非常高明的爬虫!)
业务流程与状态转换机
其业务流程主要是模拟人在浏览器里的操作过程,从登陆到搜索,从搜索再到提交,从提交再到获取提交状态,如果AC了就转到下一题,如果没有AC就进行第二次尝试(每道题目进行10次尝试),如果中间出现异常就直接进行下一次尝试,来保证程序顺利进行。其整个过程通过下面四个状态变量来控制。状态转换正常情况下发生在webBrowser1_DocumentCompleted以及timer1_Tick事件中,其中前一个事件是每次web文档加载完毕时响应,后一个事件是每隔一定时间响应,此外当代码提取和状态提取的web文档解析线程中如果发生异常,也会触发状态转变。
1 /// <summary> 2 /// 0初始状态;1填写用户名和密码状态;2输入找到的代码;3查看是否AC; 3 /// </summary> 4 static int input_state = 0; 5 /// <summary> 6 /// 0初始状态;1移动鼠标聚焦name和password输入;2移动鼠标聚焦code输入 7 /// </summary> 8 int mouse_state = 0; 9 /// <summary> 10 /// 0初始页面;1登陆页面;2提交代码页面;3查看AC页面 11 /// </summary> 12 int page_state = 0; 13 /// <summary> 14 /// 0初始情况;1已搜索链接;2代码解析中;3代码解析完毕 15 /// </summary> 16 /// <summary> 17 /// 0:初始状态;1:Queuing状态;2:Accepted状态;3:错误状态 18 /// </summary> 19 private static int AC_state = 0;
搜索答案
本程序的核心部分在于爬代码的爬虫的设计,这里调用百度搜索并从搜索结果中解析相关链接,然后转到对应链接提取答案代码。首先我想到的是一般对ACM题目写解答的地方一般是博客,所以顺手写了个从百度搜索结果获取以http://开头含blog的链接作为目标链接C#代码,用下面的代码顺利找到了杭电1202题的三个可能含有解答代码的链接放在D:\\1.txt中:(下面是搜索结果,前一个链接为代码链接)
[1]http://blog.csdn.net/vsooda/article/details/7989833 [2]http://blog.csdn.net/libin56842/article/details/8798301 [3]http://blog.csdn.net/lulipeng_cpp/article/details/7496022
但是,很快我发现这并不是一个很好的主意!因为通过解析百度搜索结果的html文件会发现搜索结果一般一次在div id="content_left"内列出10条搜索结果,并且一般给出的链接并不是目标地址的直接链接,值得庆幸的是这10条搜索结果排列整齐,而且非直接链接输入浏览器中会转为相应的目的链接,于是又顺手写了个爬这10个链接的程序:(点击看GitHub中的源码,下面是搜到的结果)
[1]http://www.baidu.com/link?url=2O6LRDmjhJM8xn-Igu5wlgkwq5aZfdxxJ4r3RLoX_AzzJrz0vmi3sWTd96ktLE0hQvzR4ea3ejgVZGPElTh6zq [2]http://www.baidu.com/link?url=jvZjI4hHOKIzBkclLKizXM6CUHbJrWUIS3RyRUryCDKVjsszzs7bqYh7bTwqt306xZgDsIt7dMjAhG-RcdkpCQY1_UGqbXbd9FS0SEdix0u [3]http://www.baidu.com/link?url=Wf5w0vIJa319PEwImt7JAqKzbLxLSsR1FP4o6LJIwojMR9xgm3gBVvU6uTkxbgMEhJ6uvj2_aScJaZeJC9sbuz-OV4Vjr_pOS6s9MEhRclC [4]http://www.baidu.com/link?url=KzVFkFeRcnZbRd9-xQ_pSW-qEG9w49FnNk8pJafCGB5JkCTJVrydtcK9A5TAooIp7Efd1kKkg3pbSi8jdZ-5s9gYaGWRCNPpC0dVqch6aZk265kqqDFaxnAQBi6ShYFh [5]http://www.baidu.com/link?url=1P-1b_x2MtGF5ixNlsflUcv-qokmPg2U4DCcqVvQ8ZMZXhWCnnWt6DKw9HoOb7dI [6]http://www.baidu.com/link?url=nVipZInn7U4yAyPtkOZRT6N_FNDi_iqYfihdtBt7OUs3LQ_SZXZQu_PoHEsUG8kDAEQvHCUx4Xw79Bf6YybwHhzp0xBEz-buI19fPDQtXbe [7]http://www.baidu.com/link?url=Kj82Etn86GRJ19AdR3L3BPJvzlRN1K2Cvv2DrqiNFijbvk3FBTPlpnT8iB2jRYzNXQTLeqGrg7w3KhlYjfYzZxsCU4mJGWD3OZVDjIPGrRC [8]http://www.baidu.com/link?url=WFNnxqS9m-erR9iBWGUCtWP1neSEOPb_Jzi_Qz1PExLy-scAHVk6DY4d1OslE-5Ns_NsX3bb1_tfgWInj1xngq [9]http://www.baidu.com/link?url=QV5i9N8Xz7JhakxPGHsxBc8oO1zVcVMsYux105JtFB_hFwUse_9f_CKd1M2ll6vZznLsHNt6RwJvKiL2zU_-sc6MhyxL7iHmxqA9oAMigge [10]http://www.baidu.com/link?url=1kG1wvoAOwdndtSKIr5wE_1TgoYudR_xyKIRQpPK_kVPhGOKkr-qw3TJ1IcIQ3GV0Cbg7Ye_vvPEh31l2gjzpa
现在,咱们爬到了链接,那么该如何从杂乱的目标页面中找寻答案代码呢?哈哈,我就不卖关子啦!其实分析众多含有代码的页面可以发现,一般情况下代码是放在body中,而且往往以#include开头(这里只找C或C++写的代码的答案)。所以,根据这个规律我设计了一个首先定位body,然后寻找第一个#include的位置作为代码起始位置,然后找到main,接着根据花括号对称的原理找到代码的结尾位置,从而从html中扣出相应的代码行。(代码请见GitHub,其中op_getCode1(string temp, int num)是新增解析目标html的函数)。下面是10个目标页面中找代码的结果,这里仅列出前5个:
从上面得到的结果不难看出:虽然存在一些完美的代码,但是有很大一部分需要修改一下也能变成完美代码!然后我综合分析了上述代码存在的情况:①代码行号没去掉;②html的转译符没有转换成标准字符;③CSS样式没有去掉;④没有有效的换行。针对问题②和问题③粗略的写了一个op_getCode2函数来实现代码精化的问题(详细代码请见GitHub),然后将代码精简下,省去不必要的html下载,尝试下解决问题①和问题④(代码中191行,没有换行是因为<br />被我直接删除了),并多添加几个html转译符的转换,结果虽然没有解决问题①,但是搜到的代码正确率已经相当高了~于是就偷下懒,不去解决问题问题①了(嘿嘿,其实解决问题①并不难,只是我不想让程序看着太乱>_<,最终的完美版代码见GitHub)
自动登录与自动提交
进行到这里看似可以大功告成了,其实还差着远呢~我们只是获得了代码,那么该如何自动登录用户并自动提交代码呢?我想到了曾经恶搞的酷我音乐盒(软硬结合第二篇——酷我音乐盒的逆天玩法),在那次我用到了邪恶的模拟鼠标点击事件来戏弄酷我音乐盒,这次我们也要这么干!yes,用程序发送鼠标事件来伪装成人在操作~但是,如果直接在IE、Google或者火狐浏览器里来操作,不就有点在玩弄这些软件的嫌疑嘛,上次恶搞酷我人家没来找我,这次可不一定呀!哈哈,所以俺还得另寻办法~(⌒(*^-゜)v开个玩笑,其实是因为考虑到要更好的操控web页面,所以才采用C#的webBrowser控件的)。下面是模拟鼠标点击事件的相关API,当想点击某个位置时只要调用SetCursorPos将鼠标位置设置为相应位置,然后发送一个Down和一个Up消息就完成了一次点击模拟~如果想移动滚动条只要设置位置->Down->设置新位置->Up~
1 private readonly int MOUSEEVENTF_LEFTDOWN = 0x2; 2 private readonly int MOUSEEVENTF_LEFTUP = 0x4; 3 [DllImport("user32")] 4 public static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo); 5 [DllImport("user32.dll", EntryPoint = "SetCursorPos")] 6 private static extern int SetCursorPos(int x, int y); 7 [DllImport("user32.dll")] 8 public static extern bool GetCursorPos(out Point pt);
可是问题又来了,如果只能用鼠标点点肯定是不能完成复杂的提交代码工作的!于是想到了另一个邪恶的东西:模拟键盘消息~嘻嘻,这次我就能往对应的地方输东西了。 如下:当鼠标将输入聚焦移动到用户名的输入框后,调用SendKeys向对应对话框内输入用户名,然后发送Tab键转至密码输入框,输入密码,最后发送回车,实现登陆!哈哈,怎么样?够不够炫酷呢?用我刚刚介绍的2个邪恶的工具再结合我玩酷我音乐盒时用的获得窗口句柄来控制窗口的手段,大家应能能想到对QQ聊天做点什么邪恶的事啦(是不是想到做一个和别人自动聊天的机器人呢?)
别得意太早,上面只是我们自以为软件会很听话的SendKeys("A")它就往框里输入A,可实际情况要糟糕的多~因为有输入法的存在,所以在我尝试往提交代码的框里发送code代码时问题就出现了~试一下,当你的系统处于智能中文输入法时,你想在某个框内输入诸如"wos1"时会出现什么效果呢?那么,我们如何来解决这个问题呢?哈哈,我又想到了办法——利用剪切板:我们板code复制到剪切板,然后在对应的代码提交框内只要发送Ctrl+V即可实现代码粘贴(我真是太奸诈啦,哈哈哈哈)
零碎的细节
到这里基本上已经算完成了,但是一个没有反馈的系统怎么能算智能呢?于是我又对HDU OJ的提交代码后的状态list页面进行解析,找到我的最新提交结果:如果AC了就做下一题,如果正在测试就再次刷新一遍知道有确定结果,如果错了就尝试下一个链接,直到10个链接全部试完转到下一题。这里要特别说明下,由于html下载与解析会造成长延时,如果放在主线程中去做会导致程序一卡一卡的,于是我将isAccept函数和另外两个分别用于找10个链接的函数operator0、对某一个链接的目标页面进行解析提取代码的函数op_getCode1分别采用线程异步计算,这里和上面实验过程中采用的主线程中直接处理有不同,要稍微注意下。更多请看详细代码:https://github.com/beautifulzzzz/C4plus/tree/master/hdu_AC自动机
后记
这几天忙着准备2015实习面试,所以也没太多时间研究这些好玩的东西啦~谨以此篇博客证明我还活着,哈哈!其实楼主脑子里还有很多炫酷的事,比如让机器自编程、让一些低级CPU学会交流与联合、在全网内养殖虚拟机器人控制舆论...不过,这都要放放,首先得解决温饱问题,不然注定孤独寂寞冷呀~
相关链接
HDU AC自动机最终工程:https://github.com/beautifulzzzz/C4plus/tree/master/hdu_AC自动机
HDU AC自动机实验版(要会用Github看历史版本!):https://github.com/beautifulzzzz/C4plus/tree/master/爬虫
本文转自beautifulzzzz博客园博客,原文链接:http://www.cnblogs.com/zjutlitao/p/4337775.html,如需转载请自行联系原作者
[C#] 逆袭——自制日刷千题的AC自动机攻克HDU OJ相关推荐
- Android应届毕业生“过五关斩六将”,怒刷千题,让你面试一路畅通!
面试:如果不准备充分的面试,完全是浪费时间,更是对自己的不负责! 如果想变得优秀那就去看那些优秀的人在学什么,做什么,想尽一切办法和他们交流:多出去看看这个世界,然后把自己逼到往死里学. 如今已是五月 ...
- 牛客网日刷30题错题解析
1.已知一棵完全二叉树的第 6 层(设根为第 1 层)有 8 个叶结点,则该完全二叉树的结点个数最多是 ? 我选的是39,正确答案为111.错点在于我理解的该树深度为6的情况,忽略了深度为7的情况,其 ...
- 【AC自动机】HDU 2222 Keywords Search 裸题
题意:给出的n个单词,出现在T中的个数. #include <stdio.h> #include <string.h> #include <stdlib.h> #i ...
- 【文学文娱】《屌丝逆袭》-出任CEO、迎娶白富美、走上人生巅峰
本文地址:http://www.cnblogs.com/aiweixiao/p/7759790.html 原文地址:(微信公众号) 原创 2017-10-30 微信号wozhuzaisi 程序员的文娱 ...
- 面试学习+刷题笔记分享-屌丝的逆袭之路,2年5个月13天,从外包到拿下阿里offer
开篇介绍 个人背景: 不说太多废话,但起码要让你先对我有一个基本的了解.本人毕业于浙江某二本院校,算是科班出身,毕业后就进了一家外包公司做开发,当然不是阿里的外包,具体什么公司就不透露了,在外包一呆就 ...
- 百度深度学习paddlepaddle7日打卡——Python小白逆袭大神学习心得
百度深度学习paddlepaddle7日打卡--Python小白逆袭大神学习心得 7日学习总结 第一天作业 第二天作业 第三天作业 第四天作业 第五天作业 7日学习总结 通过这7日打卡课程的学习,从小 ...
- 数据分析真题日刷 | 招商银行信用卡中心2019秋招IT笔试(数据挖掘方向第二批)
进入「数据分析真题日刷」系列第10篇 ⬇️ 今日真题 招商银行信用卡中心2019秋招IT笔试(数据挖掘方向第二批) (来源:牛客网) 题型 客观题:单选30道 主观题:问答4道 完成时间 120分钟 ...
- 【万人千题】誓要成为刷题界的卷王王中王
<暗里刷题>(改编自<暗里着迷>) 兄弟们,由于需要,得开始接触二次元了,明年想办法进驻b站.承诺会更新算法系列视频教程,十年内保证更新完所有算法视频.有兴趣的可以提前关注 ...
- 《逆袭进大厂》第十五弹之智力情景题 | 个人博客
卷友们好,我是阿秀. 嗯,智力题&情景题终于来了,这次阿秀不再是鸽秀... 其实真不是我不更,主要是最近事情有点多,最近在忙着个人博客和研究生毕业答辩的事情. 搭这个博客主要是因为不止一个小伙 ...
最新文章
- Cmake 交叉编译
- CentOS5.6系统下mysql5安装
- postgresql中DML操作
- you need to build uWSGI with SSL support to use the websocket handshake api function !!!
- 利用最小堆找出10亿个数中最大的10000个数
- OSChina 周一乱弹 —— 抱着漂亮袜子就亲了一口
- 连麦互动直播方案全实践2:网易云信连麦互动直播方案的演变过程
- jsp连接mysql数据库代码_JSP连接MySQL数据库代码
- ABP VNext 微服务演示,项目状态和路线图
- 由浅到深理解ROS(6)-坐标转换
- np.random.seed(0)作用
- c语言 多文件 学生系统,编的学生成绩管理系统 从文件中读取保存数据总会多读入一组乱码数据...
- python 片段_python片段程序
- iPhone 的倒计时竟然会显示假时间?
- 腾讯掌舵者马化腾,才是移动互联网的大功臣?
- Elasticsearch 日期时间处理
- Docker 安装常用软件记录
- 如何使用SVG生成超酷的页面预加载素描动画效果
- [数据结构]链表的实现在PHP中
- 实证研究的步骤_本科生毕业论文设计可以用到的研究方法有哪些