diff --git a/Leecode/src/main/java/com/markilue/leecode/string/ReverseWords.java b/Leecode/src/main/java/com/markilue/leecode/string/T04_ReverseWords.java similarity index 98% rename from Leecode/src/main/java/com/markilue/leecode/string/ReverseWords.java rename to Leecode/src/main/java/com/markilue/leecode/string/T04_ReverseWords.java index 9205569..eeb0bb7 100644 --- a/Leecode/src/main/java/com/markilue/leecode/string/ReverseWords.java +++ b/Leecode/src/main/java/com/markilue/leecode/string/T04_ReverseWords.java @@ -19,7 +19,7 @@ import java.util.stream.Stream; * 注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。 * @Version: 1.0 */ -public class ReverseWords { +public class T04_ReverseWords { @Test public void test() { diff --git a/Leecode/src/main/java/com/markilue/leecode/string/T05_ReverseLeftWords.java b/Leecode/src/main/java/com/markilue/leecode/string/T05_ReverseLeftWords.java new file mode 100644 index 0000000..27f7f2b --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/string/T05_ReverseLeftWords.java @@ -0,0 +1,92 @@ +package com.markilue.leecode.string; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.string + *@Author: dingjiawen + *@CreateTime: 2022-12-30 11:48 + *@Description: + * TODO 力扣 剑指offer58题 左旋转字符串: + * 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。 + * 比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。 + *@Version: 1.0 + */ +public class T05_ReverseLeftWords { + + @Test + public void test(){ + String s = "abcdefg"; + int k = 2; + System.out.println(reverseLeftWords(s,k)); + } + + @Test + public void test1(){ + String s = "lrloseumgh"; + int k = 6; + System.out.println(reverseLeftWords(s,k)); + } + + /** + * 思路:和T04很像,比T04简单 + * 速度击败52.3%,内存击败28.97% + * @param s + * @param n + * @return + */ + public String reverseLeftWords(String s, int n) { + StringBuilder builder = new StringBuilder(); + char[] chars = s.toCharArray(); + int right=n; + while (n= start) { + if (chars[end] != ' ' || chars[end + 1] != ' ') { + newChar[k++] = chars[end--]; + } else { + end--; + } + } + char[] result = Arrays.copyOf(newChar, k); + //反转内部单词 + int left = 0; + int right = 0; + while (right < result.length) { + if (right == result.length - 1) { + reverse(result, left, right); + } + if (result[right] == ' ') { + reverse(result, left, right - 1); + left = right + 1; + } + right++; + } + return new String(result); + } + + + public void reverse(char[] chars, int start, int end) { + char temp; + while (start < end) { + temp = chars[start]; + chars[start] = chars[end]; + chars[end] = temp; + start++; + end--; + } + } + + + /** + * 官方最快:省去了反转时间 + * 速度击败100%,内存击败74.78% 1ms + * @param s + * @return + */ + public String reverseWords1(String s) { + char[] c = s.toCharArray(); + char[] newC = new char[c.length + 1]; + int i = c.length - 1; + int index = 0; + while(i >= 0){ + while(i >= 0 && c[i] == ' ') --i;//删除前空格 + int right = i; + while(i >= 0 && c[i] != ' ') --i;//找到第一个空格 + //找到左右入口单词直接填入即可,剩下的单词类似于递归,继续重新开始上面的步骤 + for(int left = i + 1; left <= right; ++left){ + newC[index++] = c[left]; + if(left == right){ + newC[index++] = ' '; + } + } + } + if(index == 0){ + return ""; + } + else{ + return new String(newC, 0, index - 1);//左闭右开的 + } + } +} 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 new file mode 100644 index 0000000..2a8fd25 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/string/second/T06_StrStr.java @@ -0,0 +1,247 @@ +package com.markilue.leecode.string.second; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.string.second + *@Author: dingjiawen + *@CreateTime: 2022-12-30 12:05 + *@Description: + * TODO 二刷力扣28题 找出字符串中第一个匹配项的下标: + * 给你两个字符串 haystack 和 needle + * 请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。 + * 如果 needle 不是 haystack 的一部分,则返回 -1 。 + *@Version: 1.0 + */ +public class T06_StrStr { + + @Test + public void test() { + String haystack = "sadbutsad", needle = "sad"; + System.out.println(strStr1(haystack, needle)); + } + + @Test + public void test1() { + String haystack = "leetcode", needle = "leeto"; + System.out.println(strStr(haystack, needle)); + } + + @Test + public void test2() { + String haystack = "hello", needle = "ll"; + System.out.println(strStr(haystack, needle)); + } + + @Test + public void test3() { + String haystack = "mississippi", needle = "issip"; + System.out.println(strStr(haystack, needle)); + } + + @Test + public void test4() { + String haystack = "ababaabbbbababbaabaaabaabbaaaabbabaabbbbbbabbaabbabbbabbbbbaaabaababbbaabbbabbbaabbbbaaabbababbabbbabaaabbaabbabababbbaaaaaaababbabaababaabbbbaaabbbabb", needle = "abbabbbabaa"; + System.out.println(strStr3(haystack, needle)); + } + + /** + * 思路:试试动态规划法:有问题,一旦匹配不上,后面就一直匹配不上了,虽然好像可以改状态转移方程,但是改完之后定义就变了 + * TODO 动态规划五部曲:暂时有问题 + * 1.dp定义: dp[i][j]表示使用haystack[0-j]能否匹配上needle[0-i] + * 2.dp状态转移方程:dp[i][j]有两种方式得到:使用haystack[j]和不使用haystack[j] + * 1.使用haystack[j]:现在刚匹配上 + * dp[i][j]=!dp[i-1][j-2]&&dp[i-1][j-1]&&(haystack[j]==needle[i]) + * 2.不使用haystack[j]:以前就匹配上了 + * dp[i][j]=dp[i][j-1] + * 所以dp[i][j]=(!dp[i-1][j-2]&&dp[i-1][j-1]&&(haystack[j]==needle[i]))|| (dp[i][j-1]) + * 3.dp初始化:为了方便初始化,将上述定义变为haystack[0-j-1]能否匹配上needle[0-i-1],那么dp[0][i]=true;dp[i][0]=false + * 4.dp遍历顺序: + * 5.dp举例推导:以haystack = "sadbutsad", needle = "sad"为例: + * [0 s a d b t s a d] + * i=0 t t t t t t t t t + * i=s f t t t t t t t t + * i=a f f t t t t t t t + * i=d f f f t t t t t t + * @param haystack + * @param needle + * @return + */ + public int strStr(String haystack, String needle) { + + boolean[][] dp = new boolean[needle.length() + 1][haystack.length() + 1]; + //初始化 + for (int i = 0; i < dp[0].length; i++) { + dp[0][i] = true; + } + + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + boolean flag = j - 2 >= 0 && i - 2 > 0 ? !dp[i - 1][j - 2] : true; + dp[i][j] = (flag && dp[i - 1][j - 1] && (haystack.charAt(j - 1) == needle.charAt(i - 1))) || (dp[i][j - 1]); + } + } + + for (int i = 1; i < dp[0].length; i++) { + if (dp[needle.length()][i]) { + return i - needle.length(); //找到了全匹配到的位置 + } + } + + return -1; + + } + + + /** + * 思路:尝试KMP算法。当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。 + * 速度击败45.39%,内存击败5.21% + * @param haystack + * @param needle + * @return + */ + public int strStr1(String haystack, String needle) { + if (needle == null || needle.length() == 0) { + return 0; + } + int[] next = getNext1(needle); + int index = -1;//开始位置 + + for (int i = 0; i < haystack.length(); i++) { + while (index >= 0 && needle.charAt(index + 1) != haystack.charAt(i)) { + index = next[index]; + } + if (needle.charAt(index + 1) == haystack.charAt(i)) { + index++; + } + if (index == needle.length() - 1) { + return (i - needle.length() + 1); + } + } + + return -1;//没找到 + + } + + + /** + * 官方获取next数组的方法:精髓在于这次的next[i]可以通过上一次的next[i-1]来获取,分为两种情况: + * 1)如果next[i-1]的下一个数和next[i]的下一个数相等,那么最大子串一定是在上一个的基础上+1,对应下面的if的内容 + * 2)如果next[i-1]的下一个数和next[i]的下一个数不相等,那么寻找上一个最大子串,看看上一次最大子串的下一个数和next[i]的下一个数相等,如果不相等就寻找在上一次的,对应于下面的while内容 + * 下所述的看就是用来记录上一次最大子串的索引 + * @param needle + * @return + */ + public int[] getNext1(String needle) { + int[] next = new int[needle.length()]; + next[0] = -1; + int k = -1; + for (int i = 1; i < needle.length(); ++i) { + //神奇的是,k既是长度又是上一次不相等的索引 + while (k != -1 && needle.charAt(k + 1) != needle.charAt(i)) { + k = next[k]; + } + if (needle.charAt(k + 1) == needle.charAt(i)) { + ++k; + } + next[i] = k; + } + + return next; + + } + + + /** + * 官方最快 + * 速度击败100%,内存击败37.1% 0ms + * @param haystack + * @param needle + * @return + */ + public int strStr2(String haystack, String needle) { + int n = haystack.length(), m = needle.length(); + if (m == 0) { + return 0; + } + int[] pi = new int[m];//next数组 + for (int i = 1, j = 0; i < m; i++) { + while (j > 0 && needle.charAt(i) != needle.charAt(j)) { + j = pi[j - 1]; + } + if (needle.charAt(i) == needle.charAt(j)) { + j++; + } + pi[i] = j; + } + for (int i = 0, j = 0; i < n; i++) { + while (j > 0 && haystack.charAt(i) != needle.charAt(j)) { + j = pi[j - 1]; + } + if (haystack.charAt(i) == needle.charAt(j)) { + j++; + } + if (j == m) { + return i - m + 1; + } + } + return -1; + } + + + /** + * 自己尝试next不减1的做法 + * 速度击败100%,内存击败70.85% + * @param haystack + * @param needle + * @return + */ + public int strStr3(String haystack, String needle) { + int m = haystack.length(); + int n = needle.length(); + if (n == 0) { + return 0; + } + //根据needle构造一个next数组,next[i]表示needle[0-i]的最长公共子串 + int[] next = getNext(needle); + int index=0; + for (int i = 0; i < m; i++) { + while (index>0&&haystack.charAt(i)!=needle.charAt(index)){ + index=next[index-1];//匹配不上就找上一个字母在哪重复的,看看之前重复的地方能不能匹配上 + } + if(haystack.charAt(i)==needle.charAt(index)){ + index++;//匹配上就试试下一个能不能匹配上 + } + if(index==n){ + //匹配到最末尾了 + return i-index+1; + } + } + return -1; + } + + public int[] getNext(String needle) { + int index = 0; + int[] next = new int[needle.length()]; + next[0] = 0; + char[] chars = needle.toCharArray(); + + + for (int i = 1; i < next.length; i++) { + //如果匹配不上了 + while (index != 0 && chars[i] != chars[index]){ + //有趣的是:index既是最长公共子串长度,又是上一次匹配上的索引,所以能这样 + index=next[index-1];//匹配不上就找上一个字母在哪重复的,看看之前重复的地方能不能匹配上 + } + if(chars[i]==chars[index]){ + index++; + } + next[i]=index; + } + return next; + + } + + +}