leecode更新

This commit is contained in:
markilue 2023-02-26 13:44:50 +08:00
parent 6e66e33c27
commit 6d39eb279d
3 changed files with 236 additions and 3 deletions

View File

@ -0,0 +1,139 @@
package com.markilue.leecode.dynamic.second;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.dynamic.second
*@Author: markilue
*@CreateTime: 2023-02-26 10:08
*@Description:
* TODO 力扣647题 回文子串:
* 给你一个字符串 s 请你统计并返回这个字符串中 回文子串 的数目
* 回文字符串 是正着读和倒过来读一样的字符串
* 子字符串 是字符串中的由连续字符组成的一个序列
* 具有不同开始位置或结束位置的子串即使是由相同的字符组成也会被视作不同的子串
*@Version: 1.0
*/
public class T37_CountSubstrings {
@Test
public void test(){
// String s="aaa";
String s="abc";
System.out.println(countSubstrings(s));
}
/**
* 思路:和编辑距离系列有异曲同工之妙也可以通过前面的是不是来判断后面的是不是 如aa是baab是
* TODO DP五部曲:
* 1.dp定义:dp[i][j]表示以j开头,以i结尾的word是不是回文字符串
* 2.dp状态转移方程:
* if s[i]==s[j]
* dp[i][j]=dp[i-1][j+1]
* else
* 3.dp初始化: dp[i][i]=true
* 4.dp遍历顺序:
* 5.dp举例推导: 以s = "aaa"为例
* [a a a]
* a t t t
* a t t t
* a t t t
* 速度击败55.35% 内存击败43.32% 7ms
* @param s
* @return
*/
public int countSubstrings(String s) {
int length = s.length();
char[] chars = s.toCharArray();
boolean[][] dp = new boolean[length][length];
int result = length;//i=j
//将初始化蕴藏在实际for里面
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (i <= j) {
dp[i][j] = true;
} else if (chars[i] == chars[j]) {
dp[i][j] = dp[i - 1][j + 1];
if (dp[i][j]) result++;
}
}
}
return result;
}
/**
* 代码随想录解法只有当char[i]==char[j]的时候才进行判断
* 速度击败62.97% 内存击败25.91% 4ms
* 快在只需要判断下三角
* @param s
* @return
*/
public int countSubstrings1(String s) {
boolean[][] dp = new boolean[s.length()][s.length()];
int result = 0;
char[] chars = s.toCharArray();
// for (int i = 1; i < dp.length; i++) {
// for (int j = i ; j >= 0; j--) {
// if(chars[i]==chars[j]){
// if(i-j<=1){//情况一a 情况二aa
// result++;
// dp[i][j]=true;
// }else if(dp[i-1][j+1]){//情况三bab
// result++;
// dp[i][j]=true;
// }
// }
//
// }
// }
for (int i = 0; i < dp.length; i++) {
for (int j = i ; j >= 0; j--) {
if(chars[i]==chars[j]&&((i-j<=1)||dp[i-1][j+1])){
//三种情况简化后的版本
result++;
dp[i][j]=true;
}
}
}
return result;
}
/**
* 双指针法:可以以一个单词为中心;也可以以两个单词为中心
* 速度击败99.97% 内存击败61.2% 1ms
* @param s
* @return
*/
public int countSubstrings2(String s) {
int result = 0;
char[] chars = s.toCharArray();
for (int i = 0; i < s.length(); i++) {//因为for是一直向前的,所以一定不会有重复
result += extend(chars, i, i, s.length()); // 以i为中心
result += extend(chars, i, i + 1, s.length()); // 以i和i+1为中心
}
return result;
}
public int extend(char[] s, int i, int j, int n) {
int res = 0;
while (i >= 0 && j < n && s[i] == s[j]) {
i--;
j++;
res++;
}
return res;
}
}

View File

@ -0,0 +1,89 @@
package com.markilue.leecode.dynamic.second;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.junit.Test;
/**
*@BelongsProject: Leecode
*@BelongsPackage: com.markilue.leecode.dynamic.second
*@Author: markilue
*@CreateTime: 2023-02-26 10:58
*@Description:
* TODO 力扣516题 最长回文子序列:
* 给你一个字符串 s 找出其中最长的回文子序列并返回该序列的长度
* 子序列定义为不改变剩余字符顺序的情况下删除某些字符或者不删除任何字符形成的一个序列
*@Version: 1.0
*/
public class T38_LongestPalindromeSubseq {
@Test
public void test() {
// String s = "bbbab";
String s = "cbbd";
System.out.println(longestPalindromeSubseq(s));
}
/**
* 思路:还是分为当前两个字符是不是相等的两种情况由于是子序列所以存在继承关系
* TODO DP五部曲:
* 1.dp定义: dp[i][j]表示以j开头,以i结尾的子序列的最长回文子序列
* 2.dp状态转移方程:
* 1.s[i]==s[j]
* dp[i][j]=dp[i-1][j+1]+2
* 2.s[i]!=s[j] 删一个看那个更大
* dp[i][j]=max(dp[i-1][j],dp[i][j+1])
* 3.dp初始化: dp[i][i]=1
* 4.dp遍历顺序:
* 5.dp举例推导:
* 两个if速度变慢 速度击败81.32% 内存击败49.22% 27ms
* @param s
* @return
*/
public int longestPalindromeSubseq(String s) {
char[] chars = s.toCharArray();
int[][] dp = new int[chars.length][chars.length];
for (int i = 0; i < dp.length; i++) {
for (int j = i; j >= 0; j--) {//由于需要j+1
if (chars[i] == chars[j]) {
if (j == i) dp[i][j] = 1;//包含初始化
else 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[chars.length - 1][0];
}
/**
* 优化:一个if,初始化放外面
* 速度击败99.36% 内存击败15.5% 13ms
* @param s
* @return
*/
public int longestPalindromeSubseq1(String s) {
char[] chars = s.toCharArray();
int[][] dp = new int[s.length()][s.length()];
for (int i = 0; i < dp.length; i++) {
dp[i][i]=1;
}
for (int i = 1; i < dp.length; i++) {
for (int j = i-1; j >=0 ; j--) {
if(chars[i]==chars[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[s.length()-1][0];
}
}

View File

@ -115,7 +115,7 @@ public class T06_StrStr {
if (needle.charAt(index + 1) == haystack.charAt(i)) { if (needle.charAt(index + 1) == haystack.charAt(i)) {
index++; index++;
} }
if (index == needle.length() - 1) { if (index == needle.length() - 1) {//全部都匹配上了,直接返回指定位置
return (i - needle.length() + 1); return (i - needle.length() + 1);
} }
} }
@ -125,6 +125,11 @@ public class T06_StrStr {
} }
@Test
public void testk(){
String s="aabaaf";
System.out.println(getNext1(s));
}
/** /**
* 官方获取next数组的方法精髓在于这次的next[i]可以通过上一次的next[i-1]来获取分为两种情况 * 官方获取next数组的方法精髓在于这次的next[i]可以通过上一次的next[i-1]来获取分为两种情况
* 1如果next[i-1]的下一个数和next[i]的下一个数相等那么最大子串一定是在上一个的基础上+1,对应下面的if的内容 * 1如果next[i-1]的下一个数和next[i]的下一个数相等那么最大子串一定是在上一个的基础上+1,对应下面的if的内容
@ -140,10 +145,10 @@ public class T06_StrStr {
for (int i = 1; i < needle.length(); ++i) { for (int i = 1; i < needle.length(); ++i) {
//神奇的是,k既是长度又是上一次不相等的索引 //神奇的是,k既是长度又是上一次不相等的索引
while (k != -1 && needle.charAt(k + 1) != needle.charAt(i)) { while (k != -1 && needle.charAt(k + 1) != needle.charAt(i)) {
k = next[k]; k = next[k];//不相等就取k+1的上一个词即k的next进行回退
} }
if (needle.charAt(k + 1) == needle.charAt(i)) { if (needle.charAt(k + 1) == needle.charAt(i)) {
++k; ++k;//找到相等的了,k++
} }
next[i] = k; next[i] = k;
} }