干货预警:所有文章都会首发于我的公众号【甜姨的奇妙冒险】,欢迎watch。
一、来历:
力扣从3月开始开启了每日一题打卡活动,于是跟风加入了打卡大军,这两天写评论、发题解,没想到反响还不错,收到了来自很多同学的鼓励与好评,短短几天就170的关注了,有的同学说每天都会蹲我的评论和题解哈哈 ,本来觉得wx公众号是比较私人的趣味(つд⊂),但不由萌生了在公众号开一个专栏写每日一题的想法,有啥问题或想法可以私信我,anytime。本文重点在第五部分细节说明。
二、原题描述:
三、题目分析:
这题是道简单的构造性题目,只需要尽可能的左右对称地构造字符串就行了。所以最终的回文串中所有的字符都左右对称地出现了偶数次(对于奇数长度的回文串,最中间的那个字符可以出现奇数次)。
比如偶数长度的回文串 “abba”,每个字符都出现了偶数次;
比如奇数长度的回文串“abcbcbcba”,c出现了奇数次,其它字符都出现了偶数次。
四、AC代码:
class
再提供Java8的流式写法:
class
五、细节说明(咳咳..划重点~~)
代码本身很简单,但有很多细节我觉得才是最重要的。【一脸认真哦 ξ( ✿>◡❛)
1. 计数为啥用数组?
counter计数是很常见的场景了,对于数据范围有限的计数,直接用数组就行了,看见很多同学用的HashMap,额外增加了hash的计算成本、链表(树)Node的创建开销等,在时间、空间上都不讨好哟。
2. 为啥counter数组的长度是58?
因为A~z的范围是'z' - 'A' + 1= 122 - 65 + 1 = 58,打印看看就知道了(但我觉得常见的几个ascii码记住的话日常会方便很多哟,比如'A'是65,'a'是97)。
3. x - (x & 1) 是什么意思?
合理的运用位运算可以让代码减少一些不必要的判断分支。
如果 x 是奇数,x & 1 的结果就是1,偶数就是0,实现了偶数不变、奇数减1的逻辑。当然这里还有另外一种位运算写法: (x >> 1) << 1 ,先右移一位去掉最末位的0或1,再左移一位,也实现了偶数不变、奇数减1的逻辑。
4. 这里为啥不写成x - x % 2?
取模是一个消耗较大的操作,因此大多数语言的编译器比如C++都对模运算进行了优化,比如可以将 x%2 优化成 x&1,这种情况下怎么写都一样啦。但是Java比较特殊,Java中是不存在无符号整型的,数字是用补码来表示的(大家还记得补码吧...最高位是符号位,0表示正数,1表示负数),所以对于负数来说,%2 与 &1 两个操作是完全不同的(如下):
int
因此我觉得jvm很难对模运算进行优化(当然也有可能优化,比如先判断一下是不是正数,但我觉得这么麻烦的话,可能并没有优化 )总之,结论就是:能用 & 就不要取模。这里提一下一个常见的规律,对于模数是2的幂的情况下,都可以优化成&运算,如下:
int
5. 流式写法看不懂?以及流式写法这里为啥慢?
s.chars()的返回值是一个 IntStream,就是Int的流;
.boxed()会装箱返回Stream<Integer>;
.collect()是聚合的算子,Collectors.toMap的三个参数分别是 keyMapper,valueMapper 和 mergeFunction。分别表示聚合出来的 Map 的 key 是什么,value 是什么,如果遇到key相同的,怎么合并值。
counter.values() 返回的是map值的集合Collection<Integer>,先用.stream()转成流以后,利用mapToInt 转成 IntStream,因为 IntStream 是支持 sum 算子的,通过sum算子进行求和。
最后,至于为啥这里流运算比传统循环慢,是因为对于小数据集无法发挥流的优势,这里只是为了介绍这种优雅的写法。
六、类似题目
Easy:
1. 判断是否是回文串(125. 验证回文串)
2. 判断是否是回文链表 (234. 回文链表)
3. 判断是否是回文数 (9. 回文数)
Medium:
4. 符串里有多少个回文子串(647. 回文子串)
5. 找到最长的回文子串(5. 最长回文子串)
关于 最长回文子串, 还有一个特定的算法叫做 Manacher算法,该算法巧妙地利用插入不存在的字符将回文串都转成奇数回文串,比如abba变成了#a#b#b#a#(字符串中心是#), abcba变成了#a#b#c#b#a#(字符串中心是c) ,有兴趣的同学可以去学习学习~
以上,蟹蟹宝宝们的观看~(๑ơ ₃ ơ)♥
恕我直言,在座的各位都是dalao!!