再也不怕回文字符串的dp了
7.29面 tiktok测开一面
采用例题+自己理解的方式解决这个系列的问题
第一题:回文字符串的个数
递归五部曲:
- 确定dp含义,dp[i][j]是i到j是否是回文字符串
- 确定递推公式,
s.charAt(i)!=s.charAt(j)不用看了
s.charAt(i)==s.charAt(j):
- "a","aa" 这一种长度小于2的肯定是的
- "a???a" 这种只能看中间的是否是回文字符串了所以和f[i+1][j-1]有关
- dp数组如何初始化 都是false f[i][i]的情况上面考虑到了
- 递归顺序,根据地推公式,需要从左下往右上推,所以横排倒序,竖排正序
5.举例推导dp数组 6->2 9->4 10->5
class Solution {
public int countSubstrings(String s) {
int len = s.length();
int ans = 0;
boolean[][] f = new boolean[len][len];
for(int i=len-1;i>=0;i--){
for(int j = i;j<len;j++){
if(s.charAt(i)==s.charAt(j)){
if(j-i<=1){
f[i][j] = true;
ans++;
}else if(f[i+1][j-1]){
ans++;
f[i][j] = true;
}
}
}
}
return ans;
}
}
第二题:最长回文子序列
这题相较于上面一题的不同就是递推公式d:
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], Math.max(dp[i][j], dp[i][j - 1]));
}
这里是序列,所以 asca这个的长度也是2
如果是ascb 则需要看asc和scb的长度谁长
这里我看代码随想录还比较了
dp[i][j] = Math.max(dp[i + 1][j], Math.max(dp[i][j], dp[i][j - 1]));
public class Solution {
public int longestPalindromeSubseq(String s) {
int len = s.length();
int[][] dp = new int[len + 1][len + 1];
for (int i = len - 1; i >= 0; i--) { // 从后往前遍历 保证情况不漏
dp[i][i] = 1; // 初始化
for (int j = i + 1; j < len; j++) {
if (s.charAt(i) == s.charAt(j)) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][len - 1];
}
}
第三题:最长回文子串
再回头看这题 难点就是,如何确定dp数组的含义了,这题虽然是求最长的,但是还是用了boolean数组,额外维护一个最长的区间就行了。
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
boolean[][] f = new boolean[len][len];
int maxLen = 0, l=0, r=0;
for(int i=len-1;i>=0;i--){
for(int j=i;j<len;j++){
if(s.charAt(i)==s.charAt(j)){
if (j - i <= 1) { // 情况一 和 情况二
f[i][j] = true;
} else if (f[i + 1][j - 1]) { // 情况三
f[i][j] = true;
}
}
if(f[i][j] && j-i+1>maxLen){
maxLen = j-i+1;
l = i;
r = j;
}
}
}
return s.substring(l,r+1);
}
}
总结
代码还是自己敲一遍熟悉。。。。面试手撕高压环境没有熟练度和思维根本不可能做出来
但是总结来说,除了一些板子题,最难的还是dp数组的定义和推导公式了,这个是最难的。
转载自:https://juejin.cn/post/7261213053314809893