ARTS是什么?
Algorithm:每周至少作一个leetcode的算法题;
Review:阅读并点评至少一篇英文技术文章;
Tip:学习至少一个技术技巧;
Share:分享一篇有观点和思考的技术文章。java
LeetCode 132. Palindrome Partitioning II程序员
思路分析
web
给定一个字符串,把它拆分红回文字符串,假设一分为二(切一刀)为一次,问最少要拆分多少次。看到最小这个关键词,而后再看这个问题,发现原问题是能够分解成更小的子问题的,好比一刀切成两个字符串,这两个字符串就是原问题的子问题,且 原问题的解 = 子问题1的解 + 子问题2的解 + 1
,所以首先想到的就是动态规划,可是这里困扰我蛮久的是状态的定义和递推方程的推导。一开始我定义的动态规划数组只有一维,dp[i] 表示问题 [0, i] 的解
,我感受这样的状态定义简单,并且递推方程也特别的好写,即 dp[i] = dp[j] + 1
,这里的 j 是在 [0,i) 之间的一个数,咱们只须要保证 [j, i] 是回文串,在遍历的过程当中,已经记录了 [0, j]
的最优解,结果记录在 dp[j]
中,因而我写出了下面的代码算法
参考代码(优化前)数据库
public int minCut(String s) {
if (s == null || s.length() <= 1) {
return 0;
}
int n = s.length();
char[] sArr = s.toCharArray();
int[] dp = new int[n];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
for (int i = 1; i < n; ++i) {
if (validation(sArr, 0, i)) {
dp[i] = 0;
continue;
}
for (int j = 0; j < i; ++j) {
if (dp[j] != Integer.MAX_VALUE && validation(sArr, j + 1, i)) {
dp[i] = Math.min(dp[i], dp[j] + 1);
}
}
}
return dp[n - 1];
}
private boolean validation(char[] sArr, int i, int j) {
while (true) {
if (i >= j) {
break;
}
if (sArr[i++] != sArr[j--]) {
return false;
}
}
return true;
}
复制代码
优化思路
编程
上面给出的解是能够 work 的,可是你能够看到的是这里重复不断地去判断一个子串是否是回文串花费了大量的时间,直接一点的优化思路就是在这个上面作文章,用一个二维的 boolean 数组来储存字符串的每一个区间是否是回文串,可是这样作仍是免不了去进行大量的判断回文串的工做。若是须要进一步优化,仔细看看回文串,其有个特色就是 str1 = c1 + str2 + c2
,若是要保证这里的 str1 是回文串,需有两个条件,str2 是回文串,并且头尾两个字符必须相等,即 c1 = c2
,这里的子问题是 str2,遍历的方法仍是和上面不变,可是如今咱们须要一个二维的数组用来记录回文串的状况,更新与遍历同步进行,不须要反复地判断一个字符串是否是回文串,这样就利用空间换时间的方式,大大提高了程序运行时的效率,代码以下数组
参考代码(优化后)网络
public int minCut(String s) {
if (s == null || s.length() <= 1) {
return 0;
}
int n = s.length();
char[] sArr = s.toCharArray();
boolean[][] isPalindrom = new boolean[n][n];
int[] dp = new int[n];
for (int i = 0; i < n; ++i) {
int min = Integer.MAX_VALUE;
for (int j = 0; j <= i; ++j) {
if ((sArr[j] == sArr[i]) && (j + 1 >= i - 1 || isPalindrom[j + 1][i - 1])) {
isPalindrom[j][i] = true;
if (j == 0) {
min = 0;
} else {
min = Math.min(min, dp[j - 1] + 1);
}
}
}
dp[i] = min;
}
return dp[n - 1];
}
复制代码
两篇关于高效学习编程文章,其中第二篇文章是第一篇的一个引用:
数据结构
The Key To Accelerating Your Coding Skills框架
第一篇主文章中给出了一个观点就是,高效学习编程主要在于渡过 “转折点”,什么是 “转折点” ?做者解释学习编程实际上是有两个阶段,第一个阶段是模仿以及积累知识,在这个阶段咱们主要看重的是区域性的知识的积累和学习,好比如何在一门不熟悉的语言中写 for 循环,新建函数、对象,访问数据库,调用恰当的库函数等等,这个阶段咱们主要是跟着一些教程来作项目,在这个过程当中咱们会熟悉并掌握一些基本的技能。到了第二个阶段,咱们则须要用本身的能力去解决一个问题,完成一个项目,这时咱们没有能够参照的教程以及攻略文档,这时咱们的心态跟第一阶段的时候发生了很大的变化,第一阶段中,咱们明确知道手头的项目是确定能够完成的,由于咱们有教程,有答案,咱们内心没有任何的顾虑;以前说的 “转折点” 就是指第一阶段过渡到第二个阶段的那个时期,在此之中,咱们再也不像以前那样,有一个明确的方向,咱们经常须要摸着石头过河,试着解决本身历来没有遇到过的问题,这时你会感受很痛苦,缘由文章也说了,由于在 “转折点” 中,你的解决问题的速度会相比第一个阶段更慢,在第二阶段咱们学的再也不是积累技能和知识点,而是过程性的学习,说的更直白些就是如何将以前学到的知识点串起来解决实际的问题,由点到线,这实际上是一个思惟维度的跳跃,难,是正常的。
做者同时也给出了如何快速渡过 “转折点” 的建议,首先就是不要放弃,要知道学习编程的人都有这么一个过程,如今的困难只是暂时的,不是永久的;另外就是学习看技术文档,而不是反复看手把手的教程。他同时指出,学习网络开发实际上是有两个转折点,这两个转折点会同时到来,一个是对 web 框架的初步掌握,到理解,再到熟练运用;另一个是算法和数据结构的学习,咱们必须学习一些常见的算法和数据结构,这样才能使咱们的思考更加的高效和优化。
在第二篇文章中,做者给了一个关于如何高效解决程序中出现的问题,以及如何提高这方面的能力。程序中出问题,对于编程新手或者编程老手都是再正常不过的,可是新手和老手的心态彻底是不同的,新手会以为本身又遇到难题了,很心烦,而老手则会思考问题的方方面面,如何快速解决这个问题,解决问题其实就是简单的两个步骤
不少人,包括我不少时候第一个步骤没有作或者是没有作完就开始执行第二个步骤,这其实并非高效的方法,解决问题的难点永远在于准确的定位到问题,这就好像你的钱包掉了,你首先得清楚地想一想是在哪掉的,若是你定位的范围比较小,那么找起来就会很是轻松。固然,做者也说到这实际上是一个过程,一开始咱们遇到问题,去 Google 上面搜索解决问题的解的时候可能须要打开 10 个页面,但随着咱们对语言、框架等一些东西的了解,咱们遇到问题就会很快定位,这时咱们再去 Google 上面搜索可能只须要打开 2 个页面。
第一篇文章的最后,做者也说了,编程就是一个持续学习的过程,咱们学习并掌握了一个技术的方方面面,这时咱们又会去学习下一个技术,接触本身从未接触过的领域,接受下一个更难的挑战,咱们每到一个温馨区就要想着如何跳出去,寻找下一个挑战,持续学习才是如何学好编程的最好答案。
此次分享一些字符编码的知识,以前看到众多的字符编码,不知道它们之间的关系,以及为何会有这种编码?它们是怎么演变而来的?它们解决了什么问题?但愿之后遇到字符编码问题,至少心中再也不不知所措,首先,咱们来了解几个关键词:
下面是我画的一张图,算是一个小型知识地图吧,能够大概描述字符编码的演变,以及相关编码方法的由来,能够先看看,图中的文字描述是指遇到的问题
计算机是只能识别数字的,要让计算机识别字符和文字,咱们须要把字符和文字转换为数字,因而有了字符编码的存在。最开始,科学家门发明了最简单的字符编码,ASCII 码,这种编码方式只涵盖了英文大小写字母以及一些经常使用的符号,还有一些控制符号。这里咱们使用一一对应的方式来查表进行字符的编码和解码,这里必须强调的是字符和数字必须一一对应,不然会出现一样的字符在计算机上显示出来的东西不同。另外有一点就是,ASCII 码是单字节的编码方式,也就是只有 8 个 bits 用来表示单个字符,可是这里的 ASCII 码仅使用了非符号位,也就是 7 个 bits 来表示,所以最多只能表示 127 个字符,剩下的一个 bit 用做通讯的奇偶校验。
后来人们发现不少很经常使用的字符 ASCII 码没有涵盖,因而打起了最高位的那个 bit 的注意,OEM 字符集是对 ASCII 的扩展,把最高位也考虑进来,因而 ASCII 扩展成了能够表示 256 个字符,可是 OEM 有不少个版本,对于这些版本有区别的是在 128~256 表示的字符,对于前面的 127 个字符是彻底兼容 ASCII 码的。
随着计算机的普及,不少非拉丁语系的国家也开始使用计算机,好比中国,韩国,日本,这么看来使用 ASCII 的单字节编码的方式是确定行不通了,因而出现了双字节和多字节编码方式,图中的 GBK 和 GB2312 就是双字节编码,可是编码的方式仍是查表,表的大小仍是 256,可是如今不止一张表了,有多张表,两个字节,第一个字节用来表示字符在第几张表中,第二个字节表示字符在该表中具体所在的位置(相似于行和列的关系)。你能够发如今这个时候出现了不少不一样的字符集,若是一个计算机想要解码一个文档就得安装与这个文档匹配的字符集。
多字节编码的方式虽然是解决了不少问题,可是如今的问题是不少内容不能出如今同一个文档中,一份日文的文档就得用日文的字符集,一份中文的文档就要用相似 GBK,GB2312 这样的字符集,若是说一份文档既有日文,又有中文,那么该如何解决?也就是说咱们仍是没有一个能够表示全部字符的字符集。因而这个时候有了 Unicode 字符集,它将全部的字符按频繁程度划分红了 17 个层面,每一个层面有 2 个字节的空间,咱们最经常使用的层面是 BMP 层面,基本涵盖了世界上全部的字符,注意这里说的 Unicode 只是字符集,它不是编码方法,由于使用以前的查表的方式来编码解码会使字符和字节流的耦合度太高,不利于之后的扩展,虽然每一个字符在 Unicode 表中均可以找到惟一对应的编码(Unicode 码),可是决定最终的字节流的仍是具体的字符编码。对于这里基于 Unicode 的字符编码,一开始的话是 UCS-2,它也是双字节的编码方式,可是其只考虑了 BMP 层面的字符,对于 Unicode 的其余层面的字符无法表示,因而有了 UTF-16,这里会使用 2~4 个字节来表示具体字符,也就是最少都要使用 2 个字节,可是这里会有一些效率问题,咱们常用的 ASCII 码中的东西若是用 2 个字节表示,最高位都是 0x00,这是很是没有必要的,那么因而就有了 UTF-8,其采用 1~4 个字节来编码,这种编码方式对于 ASCII 码中的内容用单字节传输,大大节省了编码效率,另外的话也解决了一些不兼容问题,好比 C 语言中的函数都将 0x00 视为字符串的末尾。这里还有一个编码方式是我国的 GB18030,由于它也能够将 Unicode 里面的全部字符都转化成字节流,全部也是 Unicode 编码方式,可是这里不一样的是,它仍是采用查表的方式来进行编码,区别于 UTF-8 和 UTF-16 的规则型编码。
这周看完了一本书,《简单的逻辑学》,读以前以为逻辑学就是那些哲学家搞的东西,可是这本书从基本的逻辑学论证开始讲起,很基础,小白都能读的懂,读完以后发现逻辑在咱们工做以及生活中无处不在,学会用逻辑去思考有助于咱们得知事情的真相,减小没必要要的焦虑,此次就来写个读书感悟吧