我们今天来看下字节跳动的校招薪资,整体来看薪资还是比较高的。非IT岗位(销售,运营等)< 测试 < 开发(前端,后端,移动端)< 算法。搞算法的工资相对来说是最高的,所以要想拿到更高的薪酬还是得搞算法,其实其他岗位也不低。
我们主要来看一下IT岗,工作地点主要集中在北京,上海,深圳和杭州。基本上都是25k以上,最高的能达到34k,并且都是15薪,每月还有各种补贴,按照平均月薪30k来算的话,年包大概能达到45w,这对于刚毕业的学生来说还是很有诱惑力的。所以要想拿高薪,毕业之后进入字节也是一个非常不错的选择。
看完了字节的校招薪资,我们再来看一道关于字节的面试题。这题是LeetCode的第5题:最长回文子串。这题是字节的常考题,除了字节考过以外,华为,美团,米哈游等大厂也都考过,我们来看下。
问题描述
来源:LeetCode第5题
难度:中等
给你一个字符串 s,找到 s 中最长的回文子串。如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例2:
输入:s = "cbbd"
输出:"bb"
- 1 <= s.length <= 1000
- s 仅由数字和英文字母组成
问题分析
这题是让找出字符串 s 的最长回文子串,这是一道非常经典的题,解法也比较多。最简单的一种解决方式就是截取字符串 s 的所有子串,然后判断它的所有子串哪些是回文串,保存最长的回文串即可,这种解法虽然简单,但时间复杂度比较高。
还一种解决方式就是中心扩散法,我们需要以每一个字符为中心往两边扩散,查找并记录最长的回文子串。查找完之后下一步我们还要以下一个字符为中心往两边扩散进行查找,这样效率也不是很高。这个时候我们可以对他进行优化,使用马拉车算法。
所以这题的最经典解法还是马拉车算法,关于马拉车算法大家可以看下我的书《算法秘籍》中第 13 章的13.2 马拉车算法,这里就不在介绍。我们这里主要讲另外一种解决方式,动态规划。
我们定义二维数组dp[length][length],如果dp[left][right]为true,则表示子串s[left,right]是回文子串,如果dp[left][right]为false,则表示子串s[left,right]不是回文子串。
如果dp[left][right]是回文子串,首先dp[left+1][right-1]必须是回文子串,并且s[left]和s[right]也必须相等,如下图所示。
1,如果s[left]!=s[right],子串s[left,right]不可能是回文子串,直接跳过即可。
2,如果s[left]==s[right],子串s[left,right]能不能构成回文子串还需要进一步判断:
2.1,如果left==right,也就是说只有一个字符,我们认为他是回文子串。即dp[left][right]=true(left==right)
2.2,如果right-left<=2,类似于"aa",或者"aba",我们也认为他是回文子串,即dp[left][right]=true(right-left<=2)
2.3,如果right-left>2,我们只需要判断dp[left+1][right-1]是否是回文子串,才能确定dp[left][right]是否为true还是false。即dp[left][right]=dp[left+1][right-1]
所以我们可以找出递推公式如下:
dp[left][right]=s[left]==s.[right]&&dp[left+1][right-1]
这里要注意,因为dp[left][right]的值要依赖dp[left+1][right-1]的值,所以我们不能从上到下一行一行的遍历。因为在二维网格中dp[left+1][right-1]是在dp[left][right]的下一行,所以我们必须先计算dp[left+1][right-1]的值,才能计算dp[left][right]。
遍历方式有多种,可以从左到右一列一列的遍历,也可以从下到上一行一行的遍历,还可以从对角线往右上角一行一行的遍历,无论哪种方式,都要保证在计算dp[left][right]的时候,dp[left+1][right-1]的值一定是计算过的。
JAVA:
public String longestPalindrome(String s) {
// start表示最长回文子串开始的位置,为了后面截取。
// maxLen表示最长回文子串的长度
int start = 0, maxLen = 1;
int length = s.length();
boolean[][] dp = new boolean[length][length];
for (int right = 1; right < length; right++) {
for (int left = 0; left < right; left++) {
// 如果两种字符不相同,肯定不能构成回文子串。
if (s.charAt(left) != s.charAt(right))
continue;
// 下面是s[left]和s[right]两个字符相同情况下的判断。
if (right - left <= 2) {
// 类似于"a","aa"和"aba",也是回文子串。
dp[left][right] = true;
} else {
// 类似于"a******a",要判断他是否是回文子串,只需要
// 判断"******"是否是回文子串即可。
dp[left][right] = dp[left + 1][right - 1];
}
// 如果字符串从left到right是回文子串,保存最长的回文子串。
if (dp[left][right] && right - left + 1 > maxLen) {
maxLen = right - left + 1;
start = left;
}
}
}
// 截取最长的回文子串
return s.substring(start, start + maxLen);
}
C++:
public:
string longestPalindrome(string s) {
// start表示最长回文子串开始的位置,为了后面截取。
// maxLen表示最长回文子串的长度
int start = 0, maxLen = 1;
int length = s.size();
vector<vector<int>> dp(length, vector<int>(length));
for (int right = 1; right < length; right++) {
for (int left = 0; left < right; left++) {
// 如果两种字符不相同,肯定不能构成回文子串。
if (s[left] != s[right])
continue;
// 下面是s[left]和s[right]两个字符相同情况下的判断。
if (right - left <= 2) {
// 类似于"a","aa"和"aba",也是回文子串。
dp[left][right] = true;
} else {
// 类似于"a******a",要判断他是否是回文子串,只需要
// 判断"******"是否是回文子串即可。
dp[left][right] = dp[left + 1][right - 1];
}
// 如果字符串从left到right是回文子串,保存最长的回文子串。
if (dp[left][right] && right - left + 1 > maxLen) {
maxLen = right - left + 1;
start = left;
}
}
}
// 截取最长的回文子串
return s.substr(start, maxLen);
}
-------------------------end-------------------------
笔者简介
博哥,真名:王一博,毕业十多年,《算法秘籍》作者,专注于数据结构和算法的讲解,在全球30多个算法网站中累计做题2000多道,在公众号中写算法题解700多题,对算法题有自己独特的解题思路和解题技巧,喜欢的可以给个关注,也可以下载我整理的1000多页的PDF算法文档。