系列文章目录
一、 数组类型解题方法一:二分法 二、数组类型解题方法二:双指针法 三、数组类型解题方法三:滑动窗口 四、数组类型解题方法四:模拟 五、链表篇之链表的基础操作和经典题目 六、哈希表篇之经典题目 七、字符串篇之经典题目 八、字符串篇之 KMP 九、解题方法:双指针 十、栈与队列篇之经典题目 十 一、栈与队列篇之 top-K 问题 十 二、二叉树篇之二叉树的前中后序遍历 十 三、二叉树篇之二叉树的层序遍历及相关题目 十 四、二叉树篇之二叉树的属性相关题目 十 五、 二叉树篇之二叉树的修改与构造 十 六、 二叉树篇之二叉搜索树的属性 十 七、二叉树篇之公共祖先问题 十 八、二叉树篇之二叉搜索树的修改与构造 十 九、回溯算法篇之组合问题 二 十、回溯算法篇之分割、子集、全排列问题 二十一、贪心算法篇之入门题目 二十二、贪心算法篇之进阶题目 二十三、动态规划篇之基础题目 二十四、动态规划篇之背包问题:01背包 二十五、动态规划篇之背包问题:完全背包 二十六、动态规划篇之经典问题:打家劫舍 二十七、动态规划篇之买股票问题(一) 二十八、动态规划篇之子序列问题:连续子序列和不连续子序列 二十九、动态规划篇之子序列问题:编辑距离 更新中 … …
前言
刷题路线来自 :代码随想录
题录
647. 回文子串
Leetcode 链接 题解: 方式一:动态规划 dp[i][j]: i 表示子串起始下标,j 表示子串结尾下标,该子串是否为字符串。 递推公式: dp[i][j] 分两种情况
- 子串长度(j - i)小于等于 2,如 a、aa, 只要起始字符等于结尾字符就是回文串
- 子串长度大于 2,如 a…a,在起始字符等于结尾字符的同时,还要保证 … 是回文串再行,而 … 是不是回文串由 dp[i + 1][j - 1] 状态决定。
注意遍历顺序结构: 第一层(起始位置)需要倒着遍历,因为首尾字符相同时,需要查看 i +1 开始 j - 1 结尾的状态值,也就是 dp[i + 1][j - 1] 状态,如果正着遍历,i + 1 下层的状态还没有更新;第二点:i,j 的含义决定第二次遍历顺序应从 j = i 开始,结尾下标 j 应大于等于起始下标 i。 返回值: 回文子串数目另外记录
class Solution {
public int countSubstrings(String s) {
int len = s.length();
boolean[][] dp = new boolean[len][len];
int res = 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 || dp[i + 1][j - 1] == true) {
dp[i][j] = true;
res++;
}
}
}
}
return res;
}
}
方式二:双指针法 先确定中间子串,如果中间子串为回文串,子串区间每次左右各扩大 1。 中间初始子串:由 1 个字符或两个字符组成。
class Solution {
public int countSubstrings(String s) {
int len = s.length();
int res = 0;
for (int i = 0; i < len; i++) {
res += extend(s, i, i, len);
res += extend(s, i, i + 1, len);
}
return res;
}
public int extend(String s, int i, int j, int len) {
int result = 0;
while (i >= 0 && j < len && s.charAt(i) == s.charAt(j)) {
i--;
j++;
result++;
}
return result;
}
}
5. 最长回文子串
Leetcode 链接
题解: 和上题类似,这里是求最长回文串 方式一:动态规划 同上题,是回文串时记录最大长度,和最大长度对应的起始末尾下标。
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
boolean[][] dp = new boolean[len][len];
int maxLen = 0;
int start = 0;
int end = 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 || dp[i + 1][j - 1] == true) {
dp[i][j] = true;
if (j - i + 1 > maxLen) {
maxLen = j - i + 1;
start = i;
end = j;
}
}
}
}
}
return s.substring(start, end + 1);
}
}
方式二:双指针
class Solution {
public String longestPalindrome(String s) {
int size = s.length();
int start = 0;
int end = 0;
for (int i = 0; i < size; i++) {
int len1 = extend(s, i, i, size);
int len2 = extend(s, i, i + 1, size);
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = start + len - 1;
}
}
return s.substring(start, end + 1);
}
public int extend(String s, int i, int j, int size) {
while (i >= 0 && j < size && s.charAt(i) == s.charAt(j)) {
i--;
j++;
}
return j - i - 1;
}
}
516. 最长回文子序列
Leetcode 链接 题解: 注意这里是子序列,指的是不连续的回文串。 状态 dp[i][j]: 表示下标从 i 到 j 的该子串的最长回文子序列长度 递推公式: 首尾字符相等时 dp[i][j] = dp[i + 1][j - 1] + 2,不相等时dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]) 举个例子: a…b 时:a 不等于 b,a…b 的最长回文子序列应该的 a… 和 …b 中的最大值。 a…a 时:a 等于 a,a…a 的最长回文子序列应该是 … 的最长回文子序列长度加上 2 (表示首尾新增的这两个 a)。
class Solution {
public int longestPalindromeSubseq(String s) {
int len = s.length();
int[][] dp = new int[len + 1][len + 1];
for (int i = 0; i < len; i++) dp[i][i] = 1;
for (int i = len - 1; i >= 0; i--) {
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];
}
}
132. 分割回文串 II
Leetcode 链接 题解: 131. 分割回文串 是一道回溯题目,在回溯篇有题解 本题在分割数组时用到动态规划,在判断是否回文时也使用动态规划。后者前边题目已经做过多次, 重点在前者,难点在弄清分割和判断回文的下标,所以这里举例说明。
判断回文先使用双指针法。 状态 dp[i]:以 i -1 结尾的子串最小分割次数 例子:aabb dp 数组:[-1,0,1,2,3] i 表示 dp 数组下标,i - 1 为子串结尾下标,j 表示新增分割线下标 i = 4 时子串就为 aabb j = 0: | aabb , aabb 不是回文串 dp[4] 值不变 dp[4] = 3 j = 1:a | abb , abb 不是回文串 dp[4] 值不变 dp[4] = 3 j = 2:aa | bb , bb 是回文串 dp[4] 值取 aa 状态 dp[2] + 1(1 表示分割一次) 和 原来dp[4] 最小值 j = 3:aab | b , b 是回文串 dp[4] 值取 aab 状态 dp[3] + 1 和 原来dp[4] 最小值
初始化每个状态为分割次数最大值:aabb 、dp 数组: [-1,0,1,2,3]
class Solution {
public int minCut(String s) {
int len = s.length();
int[] dp = new int[len + 1];
for (int i = 0; i <= len; i++) {
dp[i] = i - 1;
}
for (int i = 2; i <= len; i++) {
for (int j = 0; j < i; j ++) {
if (isPal(s, j, i - 1)) {
dp[i] = Math.min(dp[j] + 1, dp[i]);
}
}
}
return dp[len];
}
public boolean isPal(String s, int start, int end) {
while (start < end) {
if (s.charAt(start) != s.charAt(end)) {
return false;
}
start++;
end--;
}
return true;
}
}
双 dp:
class Solution {
public int minCut(String s) {
int len = s.length();
int[] dp = new int[len + 1];
boolean[][] mat = getMat(s, len);
for (int i = 0; i <= len; i++) {
dp[i] = i - 1;
}
for (int i = 2; i <= len; i++) {
for (int j = 0; j < i; j++) {
if (mat[j][i - 1]) {
dp[i] = Math.min(dp[j] + 1, dp[i]);
}
}
}
return dp[len];
}
public boolean[][] getMat(String s, int len) {
boolean[][] dp = 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 || dp[i + 1][j - 1] == true) {
dp[i][j] = true;
}
}
}
}
return dp;
}
}
|