LeetCode 009 — Palindrome Number

Palindrome Number https://leetcode.com/problems/palindrome-number/ 给定一个整型数,判断这个数是否为回文数 我们根据例子很容易得出,负数不是回文数,一位正整数肯定是回文数。 Solution1 首先用最简单的方法,把数字转换成字符串,从两边向中间依次判断对应位的数字是否相等。 class Solution { public: bool isPalindrome(int x) { if (x < 0) { return false; } if( x == 0) { return true; } std::string str = to_string(x); int middle = str.length()/2; for (int i = 0; i < middle; ++i) { if(str[i] != str[str.length() - 1 - i]) { return false; } } return true; } }; Solution2 题目的最后问能否不通过转换成字符串的方式解决这个问题,那就尝试一下吧。不通过字符串,其实我们还是要从两边到中间依次比较对应位的数字。那么问题来了,如何依次取得左右两边的数字呢? 我们以 12341 这个数字为例,想要获得最低位数字 1 很简单只要12341 % 10就得到了。那么如何获得最高位数字 1 呢?我们发现12345/10000 -> 1,也就是说用这个数除以 10^4 就行了,其中的 4 是 12341 的位数 5 减去 1。我们可以通过循环 / 10 的方式取得一个数字的位数。 ...

2019-04-20 · 2 min · 卡斯

LeetCode 008 — String to Integer

String to Integer (atoi) https://leetcode.com/problems/string-to-integer-atoi/ 题目大意就是自己实现一个 atoi 函数把字符串转换成一个整型数字 字符可能以多个空格开始,处理的时候要舍弃左边的空格,从第一个不为空格的字符开始处理; 数值字符串可能以+-号开头,要正确处理; 数值字符串后可能带有多余的非数字字符,这些字符不会对结果造成影响,处理的时候要忽略; 第一个非空格字符不是合法的数字字符或者+-号的时候,这个字符串不是合法的数值字符串,要返回 0; 结果大于 INT_MAX (2^31 − 1) 的时候返回 INT_MAX,结果小于 INT_MIN (−2^31) 的时候返回 INT_MIN 。 这题目看起来并不难,我上手一通操作提交之后,连错了三次,尴尬。。。 以下是可能碰到的部分容易出错的情况: 越界,这个字符串数值可能远远超过了32位整型所能表示的大小,甚至超过了64位。所以不要试图用 long long 来存储最终算出来的数值再跟 INT_MAX 比较,应该在计算过程中随时跟 INT_MAX 发现超过了,就可以停止计算,直接返回相应结果,这样既节省时间,又不会造成 long long 越界; 字符串以多个+-号开头,比如 “+–1234”。从第二个符号开始,并不是数字,那么这个字符串就是非法的,返回 0; 数值字符串和非数字字符中间可能并没有空格,不要被 Example 误导了,比如 “12AB”结果是 12; +-号出现在字符串的中间,比如 “34+1”。这里的+号只能当做普通的非数字字符处理,因此结果返回 34; Solution class Solution { public: int myAtoi(string str) { str.erase(0, str.find_first_not_of(' ')); int last_pos = 0; for(last_pos = 0; last_pos < str.length(); ++last_pos) { if (last_pos == 0) { if (str[last_pos] != '+' && str[last_pos] != '-' && (!(str[last_pos] >= '0' && str[last_pos] <= '9'))) { break; } } else { if (!(str[last_pos] >= '0' && str[last_pos] <= '9')) { break; } } } str.erase(last_pos); //cout<<"_"<<str<<"_"<<endl; if (str.empty()) { return 0; } if ((str[0] == '+' || str[0] == '-')) { if (str.length() == 1) { return 0; } } long long res = 0; bool is_negative = false; int i = 0; if (str[i] == '+' || str[i] == '-') { if (str[i] == '-') { is_negative = true; } ++i; } for (;i < str.length(); ++i) { res *= 10; res += str[i] - '0'; if (res > INT32_MAX) { return is_negative ? INT32_MIN : INT32_MAX; } } return is_negative ? -res : res; } }; 首先用 string::erase 配合 string::find_first_not_of(' ') 去掉字符串开头的空格; 接下来去掉数值字符串后面的多余字符,仅保留要处理的数值字符串; 这个时候可能只剩下一个空字符串或者单个+或者-字符,这里就直接返回结果; 到了真正处理数值字符串的时候了,先判断第一个字符是不是+-符号并记下正负标记; 接下来从前往后遍历,并处理数值,中途要是发现数值已经超过了32位整型就可以根据正负标记直接返回相应结果; 最后根据正负标记返回最终结果,完成! 总结 从这题的 14.6% 的通过率来看 Medium 难度不是浪得虚名,要仔细研究题目里说的可能发生的每一种情况,并做好相应的处理,不然想一次 Accept 还是有难度的。 ...

2019-04-14 · 2 min · 卡斯

Docker 部署 pyspider 踩坑实录

最近在倒腾 pyspider,不得不说这个爬虫框架用起来真的很方便。从编写调试到部署一条龙服务, 对于我这种 Scrapy 苦手来说,简直就是救星。另外 pyspider 还提供了 Docker 镜像,通过 Docker 部署省去了安装依赖的麻烦。不过我实际根据官方文档尝试 Docker Compose 部署的时候还是遇到了一点小麻烦,搜索折腾了一番之后总算弄好了,这里做个记录。 ImportError: No module named MySQLdb 执行 docker-compose up 的时候,报了这个错误,解决方法是安装 MySQL-python。 # Dockerfile FROM binux/pyspider:latest RUN pip install MySQL-python 这里大家可以自己通过上面的 Dockerfile build 一个新的镜像,或者也可以直接使用我上传的镜像kuma1430/pyspider。然后把官方的 docker-compose.yml 里的 binux/pyspider 换成你创建的镜像名或者 kuma1430/pyspider。 Authentication plugin ‘caching_sha2_password’ cannot be loaded 通过上面安装 MySQL-python,我们解决了找不到 MySQLdb 的问题,但是新的问题出现了: sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (2059, "Authentication plugin 'caching_sha2_password' cannot be loaded: /usr/lib/mysql/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory") (Background on this error at: http://sqlalche.me/e/e3q8) 这里只要把 docker run --name mysql -d -v /data/mysql:/var/lib/mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql:latest 里的 mysql:latest 改为 mysql:5.7 就行了。 ...

2019-03-04 · 1 min · 卡斯

nginx 安装和配置速记

安装 在 Ubuntu 下可以通过 apt-get 安装: sudo apt-get install nginx 安装完成后 nginx 会自动启动,这时候可以通过浏览器访问 80 端口。不出意外的话,就可以看到「Welcome to nginx!」的提示信息。 启动、关闭与重载配置 执行 nginx 命令可以直接启动 ngxin。如果 nginx 已经在运行,那么可能会出现端绑定错误提示。或者,nginx 默认的 80 端口被别的程序占用,那么也会出现这个错误。 在 nginx 已经在运行的情况下,可以通过 -s 参数对 nginx 进行控制: nginx -s signal 执行 nginx -s quit 或者 nginx -s stop 关闭 ngxin 服务。其中 quit 和 stop 的区别在于:quit 是平滑关闭,它会等待工作进程完成当前的请求之后再关闭进程,而 stop 则是强制关闭。 在对 nginx 的配置文件进行修改之后,需要执行 nginx -s reload 命令来充值配置文件,使修改生效。 配置 nginx 默认的配置文件是 /etc/nginx/nginx.conf。在这个文件中我们可以对 nginx 进行各种参数的配置。 这个文件里有 include /etc/nginx/sites-enabled/*; 这样一句话。这说明在这里加载了额外的配置文件。在 sites-enable 文件夹里只有一个 default 文件。它其实是一个软链接,指向了 /etc/nginx/sites-available/default。default 去掉无关信息之后的如下: ...

2017-06-15 · 2 min · 卡斯

Linux 笔记 — 使用 Screen 管理会话

基本使用 新建一个名为 kuma 的会话 screen -S kuma 列出当前所有会话 screen -ls 恢复名为 kuma 的会话 screen -r kuma 快捷键 ctrl+a d 断开(detach)当前会话 ctrl+a k 杀掉(kill)当前会话

2017-01-12 · 1 min · 卡斯

Boost date_time 库处理日期和时间

在游戏服务器端的开发工作中,很多时候我们不得不面对繁琐的日期和日期处理。比如说一个运营活动的开始和结束时间处理,还有一个限时道具的到期时间处理等等。如果恰好程序的开发语言是 C++,并且框架并没有对时间处理进行过合理的封装,那么这项工作看起来就不是那么轻松愉快了。这个时候使用 time_t 类型和 tm 结构体,进行原始的时间和日期的比较和计算,就俨然成了程序员的梦魇。不过好在写 Boost 的前辈们已经帮我们做了复杂的工作,然后封装成了这个date_time 库。用来真是爽歪歪啊~ date_time 概述 在使用 date_time 库之间,我们首先要明确三个基本概念:时间点(Time Point)、时长(Time Duration)、时段(Time Interval)。所谓时间点就是时间轴上的一个特定的点;时长就是两个时间点的差值,不属于时间轴;时段是两个特定时间点之间确定的区间。基本上所有的时间处理都是围绕的这三个概念进行的。这三者可以进行相互运算,例如时间点+时长=时间点,时长+时长=时长等。 处理日期 date_time 库的日期处理基于格里高利历, 支持从 1400-01-01 到 9999-12-31 之间的日期计算。为了使用 date_time 库的日期功能需要包含头文件 <boost/date_time/gregorian/gregorian.hpp>,即: #include <boost/date_time/gregorian/gregorian.hpp> using namespace boost::gregorian; date date 是 date_time 库处理日期的核心类,以天为单位表示时间点概念。 date d1(2001, 1, 1); //使用数字构造日期 date d2(2000, Jan, 1); //使用英文指定月份 date d3(d1); //date 支持拷贝构造 assert(d1 == d3); //date 支持比较操作 assert(d2 < d1); date 也可以从一个字符串产生: date d1 = from_string("1999-12-31"); date d2 = from_string("1999/12/31"); date d3 = from_undelimited_string("19991231"); day_clock day_clock 是一个天级别的时钟,它也是一个工厂类,调用它的静态成员函数 local_day() 或 universal_day() 会返回一个当天的时间对象,分别是本地日期和 UTC 日期。 date d1 = day_clock::local_day(); date d2 = day_clock::universal_day(); date_duration 日期长度是一天为单位的时长,基本的日期长度是 date_duration(days) ...

2016-08-27 · 1 min · 卡斯

LeetCode 003 — Longest Substring Without Repeating Characters

Longest Substring Without Repeating Characters https://leetcode.com/problems/longest-substring-without-repeating-characters/ 给定一个字符串,找出最长不重复子串的长度。 Solution 1 第一个解决方案依然采取最直观的方式,就是做两层遍历,从前往后逐个字符比较。 class Solution { public: int lengthOfLongestSubstring(string s) { int length = 0; for (int i = 0; i < s.length(); ++i) { int sub_length = 1; string sub_str = s.substr(i, sub_length); for (int j = i + 1; j < s.length(); ++j) { if (sub_str.find(s[j]) != string::npos) { break; } sub_str += s[j]; sub_length++; } if (sub_length > length) { length = sub_length; } } return length; } }; 提交代码,Accepted,但是 Runtime 164ms,太惨了… ...

2016-06-29 · 2 min · 卡斯

LeetCode 001 — Two Sum

最近开始撸 LeetCode,在这里做个简单的记录。撸 LeetCode 也不是因为闲来无事,只是觉得在工作之外需要继续锻炼。初步打算按照Problems 的题号来撸,Easy 优先,其他难度的撸不过可以先跳过,以后再来一遍也未尝不可,保持自信心比较重要。 Two Sum https://leetcode.com/problems/two-sum/ 题目大意是给定一个 int 数组,要求返回数组中两个数的下标值的数组,并且满足这两个数的和等于一个给定的目标值。并且假定每一个测试用例只有一个确定的结果。 Solution 1 这题最容易想到的方法,自然是最暴力的两个 for 循环的遍历,实现如下: class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { bool is_get = false; int i = 0; int j = 0; for (; i < nums.size(); i++) { for (j = i + 1; j < nums.size(); j++) { if (nums[i] + nums[j] == target) { is_get = true; break; } } if (is_get) { break; } } vector<int> v; v.push_back(i); v.push_back(j); return v; } }; 把这段代码提交上去,成功 Accepted 了,但是花费了688 ms。 ...

2016-03-22 · 2 min · 卡斯

泰拉瑞亚游戏存档路径修改

泰拉瑞亚(Terraria) 是一款非常受欢迎的2D沙盒游戏,甚至有不少玩家称之为 2D MineCraft。我久闻这款游戏的大名,所以早在半年前就从 Steam 上入了正版。但是非常遗憾的是,泰拉瑞亚的PC版没有合适的引导教程,导致入门有些门槛。比如说新手一进去不知道干什么,不知道如何使用道具、怎么砍树、怎么造房子,然后就在第一个夜晚到来的时候不停地被僵尸和眼球虐死。我自己本身也尝试了好几次,都没有摸索出合适的玩法,都没能入门玩起来,所以这款游戏就这么被我搁置了。 直到上个月,我无意中看到一个同事正在玩泰拉瑞亚的手机版,然后就下载了一个一起玩。令人意外的是手机版对于新手友好多了。手机版的开始菜单专门有一个教程栏目,点进去可以在一个教程地图上按照提示学习游戏的操作。除此之外游戏还特别针对移动版进行了优化,对一些在 PC 上比较复杂的操作在移动平台上进行了简化,大大降低了游戏的入门难度。还有一点令人惊讶的是,泰拉瑞亚的 Android 版和 iOS 版可以无缝局域网联机,甚至包括 iOS 盗版的。经过几天时间跟三四个同事一起联机摸索,我总算是入门了泰拉瑞亚。 长时间用手机玩泰拉瑞亚其实是一件挺累人的事,特别是挖矿和造房子。所以一段时间后,我又拉了几个同事一起玩 PC 版。一开始他们几个人玩的是汉化版的,我玩的是 Steam 英文版,这两个版本居然也是可以无缝局域网联机的,真是太棒了。由于游戏里的道具非常多,英文名字比较复杂,我拿到有些道具之后,不得不查词典,才能跟同事说我拿到的是什么。所以为了方便跟同事沟通,我后来也玩了汉化版的。 这里我碰到一个问题,就是 Steam 版和汉化版的游戏存档都存在 我的文档\My Games\Terraria\ 这个目录下,所以有些时候会发生冲突,造成游戏无法正常启动。所以我就想能不能把汉化版的存档目录修改一下,改成客户端所在目录,这样就方便多了。 先从客户端文件下手吧,把 Terraria.exe 扔到 PEiD 里查一下壳,发现这个东西没有加壳,是 C# 写的 .NET 程序。 既然知道是 .NET 程序,那我们就可以用 .NET Reflector 或者 ILSpy 来反编译分析。我以前没有尝试修改过 .NET 程序,这次也是新手上路。上述两个工具我都尝试过,但是在改完代码后保存可执行文件都或多或少出了一点问题。然后我找到了一个叫 JustDecompile 的反编译工具。这个工具跟大名鼎鼎的 Fiddler 是同一个公司出的,完全免费。下载安装 JustDecompile 后运行,打开 Plugins 菜单,安装 Assembly Editor 插件。这个插件其实就是 Refexlil,在上面两个工具里也可以使用这个插件,非常强大。 我们用 JustDecompile 打开 Terraria.exe, 然后点击 Open->Load Framewrok->Load .NET 4.6 Assemblies->x86 导入 x86 的 .NET 4.6 的库。接着打开 Search 菜单,左边的查找数据类型选择 Full Text, 然后查找 “My Games” 字符串。查找到一个结果,是一个名叫 GetStroragePath 的函数,双击这条结果,跳转到函数所在的代码。可以看到 JustDecompile 已经自动帮我们把反编译出来的 IL 代码转成了 C# 代码,这个函数的实现是这样的: ...

2016-01-19 · 1 min · 卡斯

后会无期

相聚有时,后会无期。

2014-07-18 · 1 min · 卡斯