真实面试案例分析(上)
摘要
- 最长回文子串Longest Palindromic Substring
- 为什么不需要学 Manacher’s Algorithm
- 基于双指针的算法与背向双指针算法简介
- 有哪些Bad Coding Style容易踩,面试真实案例分析
- 基于动态规划的算法与区间型动态规划简介
- 面试评分标准
- 快速提高Coding Quality的十二个技巧
200 · 最长回文子串
求一个字符串中最长的回文子串,回文串定义为: aba, abba 这样的正反都一样的字符串
O(n^3) 的实现方法下比较好的 Coding Quality 是什么样的:
/**
* @param s: input string
* @return: a string as the longest palindromic substring
*/
// 暴力枚举
func LongestPalindrome(s string) string {
// 最长向短遍历
for length := len(s); length > 0; length-- {
for start := 0; start+length <= len(s); start++ {
if isPalindrome(s, start, start+length-1) {
return s[start : start+length]
}
}
}
return ""
}
func isPalindrome(s string, left, right int) bool {
for left < right && s[left] == s[right] {
left++
right--
}
return left >= right
}
基于中心点枚举法 Enumeration 的最佳实践
/**
* @param s: input string
* @return: a string as the longest palindromic substring
*/
func LongestPalindrome(s string) string {
res := ""
for i := 0; i < len(s); i++ {
res = getPalindromeFrom(s, i, i, res)
res = getPalindromeFrom(s, i, i+1, res)
}
return res
}
func getPalindromeFrom(s string, left, right int, res string) string {
sub := ""
for left >= 0 && right < len(s) && s[left] == s[right] {
sub = s[left : right+1]
left--
right++
}
if len(res) < len(sub) {
return sub
}
return res
}
基于动态规划 Dynamic Programming 的最佳实践
/**
* @param s: input string
* @return: a string as the longest palindromic substring
*/
// 动态规划
// 状态转移方程: dp[i][j] = (s[i] == s[j]) && ((j-i < 3) || dp[i+1][j-1])
func LongestPalindrome(s string) string {
res, dp := "", make([][]bool, len(s))
for i := 0; i < len(s); i++ {
dp[i] = make([]bool, len(s))
}
for left := len(s) - 1; left >= 0; left-- {
for right := left; right < len(s); right++ {
dp[left][right] = (s[left] == s[right]) && ((right-left < 3) || dp[left+1][right-1])
if dp[left][right] && (res == "" || right-left+1 > len(res)) {
res = s[left : right+1]
}
}
}
return res
}
LPS 的面试考点
- 面试不一定会要求你用最优复杂度的算法来解决问题
- 因此单纯只刷LC之类的OJ,容易让你产生一定要用最优解来解决这样的误区
- 代码真的不是写出来就可以过
- 代码质量(Coding Quality)很重要
- 好的代码质量包括:
- Bug Free
- 好的代码风格(Coding Style),包括变量名命名规范有意义,合理的使用空格,善用空行
- 容易让人读懂的逻辑。要把复杂的事情用简单的方式,别把简单的事情写复杂了。
- 没有冗余代码
- 有边界检测和异常处理
LPS 的时间复杂度
- Manacher’s Algorithm - O(n) // 学有余力可以阅读全文并背诵后缀数组
- Suffix Array - O(n) // 完全不用学
- 动态规划 Dynamic Programming - O(n^2) // 必须掌握
- 枚举法 Enumeration - O(n^2) // 必须掌握
LPS 的评分体系
- Strong Hire
- 使用 O(n) 或者 O(nlogn) 的算法实现出来 (Manacher’s Algorithm or Suffix Array),并且代码质量合格,无 Bug 或者有很小的bug但是能自己发现并解决,无需太多提示
- Hire
- 能够分别使用枚举法和动态规划实现时间复杂度 O(n^2) 的算法。并且代码质量优秀,无Bug,无重复代码,无需面试官给提示
- Weak Hire
- 只使用了其中一种 O(n^2) 的算法实现出来,代码质量还不错,可以有一些小 Bug,面试官可以给一些小提示
- No Hire
- 只能想出一种 O(n^2) 的算法,但是 Bug 太多,或者需要很多提示
- Strong No Hire
- 连一种 O(n^2) 的算法都想不到
独孤九剑 —— 总决式
想要做到 Bug Free 最重要的是优化你的 Coding Quality
工程师的代码长什么样比脸长什么样重要
快速提高 Coding Quality 的十二个技巧
- Coding Style 相关:
- 二元运算符两边加空格,单元运算符不加空格
- 花括号和 for, if 之间要加空格(Java),圆括号和 if 之间要加空格
- 用空行分隔开不同的逻辑块
- 逗号后面加空格
- Readability 相关
- 函数名和变量名用1-2个单词作为名称
- 确保一个函数内部不超过 3 层缩进(indention)
- 多用子函数来减少入口函数的代码量
- 多用 continue 少用 if
- Bug Free 相关
- 不管有没有可能出问题,都要对入口函数的参数进行异常检测
- 访问一个下标的时候,一定要确保这个下标不会越界
- 访问一个对象的属性或者方法时,一定要确保这个对象不是空
- 不用全局变量
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。