diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T37_CountSubstrings.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T37_CountSubstrings.java new file mode 100644 index 0000000..4118ac7 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T37_CountSubstrings.java @@ -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; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T38_LongestPalindromeSubseq.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T38_LongestPalindromeSubseq.java new file mode 100644 index 0000000..97234c8 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/second/T38_LongestPalindromeSubseq.java @@ -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]; + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/string/second/T06_StrStr.java b/Leecode/src/main/java/com/markilue/leecode/string/second/T06_StrStr.java index 2a8fd25..375a245 100644 --- a/Leecode/src/main/java/com/markilue/leecode/string/second/T06_StrStr.java +++ b/Leecode/src/main/java/com/markilue/leecode/string/second/T06_StrStr.java @@ -115,7 +115,7 @@ public class T06_StrStr { if (needle.charAt(index + 1) == haystack.charAt(i)) { index++; } - if (index == needle.length() - 1) { + if (index == 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]来获取,分为两种情况: * 1)如果next[i-1]的下一个数和next[i]的下一个数相等,那么最大子串一定是在上一个的基础上+1,对应下面的if的内容 @@ -140,10 +145,10 @@ public class T06_StrStr { for (int i = 1; i < needle.length(); ++i) { //神奇的是,k既是长度又是上一次不相等的索引 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)) { - ++k; + ++k;//找到相等的了,k++ } next[i] = k; }