344. 反转字符串
https://leetcode-cn.com/problems/reverse-string/
- 双指针
public void reverseString(char[] s) {int L = 0;int R = s.length - 1;while (L < R) {char tmp = s[L];s[L++] = s[R];s[R--] = tmp;}}
541. 反转字符串Ⅱ
https://leetcode-cn.com/problems/reverse-string-ii/
这道题目其实也是模拟,实现题目中规定的反转规则就可以了。
一些同学可能为了处理逻辑:每隔2k个字符的前k的字符,写了一堆逻辑代码或者再搞一个计数器,来统计2k,再统计前k个字符。
其实在遍历字符串的过程中,只要让 i += (2 k),i 每次移动 2 k 就可以了,然后判断是否需要有反转的区间。
因为要找的也就是每2 k 区间的起点,这样写,程序会高效很多。
*所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。
public String reverseStr(String s, int k) {char[] ch = s.toCharArray();for (int i = 0; i < ch.length; i += 2 * k) {// 1. 每隔 2k 个字符的前 k 个字符进行反转// 2. 剩余字符小于2k但是大于或等于k个, 则反转前k个字符if (i + k <= ch.length) {reverse(ch, i, i + k - 1);continue;}// 3. 剩余字符少于k个, 则将剩余字符全部反转reverse(ch, i, ch.length - 1);}return new String(ch);}public void reverse(char[] chs, int begin, int end) {int L = begin;int R = end;while (L < R) {char tmp = chs[L];chs[L++] = chs[R];chs[R--] = tmp;}}
剑指Offer 05. 替换空格
https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/
- 法一: StringBuilder
```csharp
public String replaceSpace1(String s) {
}StringBuilder sb = new StringBuilder();for (int i = 0; i < s.length(); i++) {if (s.charAt(i) == ' ') {sb.append("%20");} else {sb.append(s.charAt(i));}}return sb.toString();
- 法二: 先扩容 再双指针【来源:代码随想录】<br />如果想把这道题目做到极致,就不要只用额外的辅助空间了!<br />首先扩充数组到每个空格替换成"%20"之后的大小。<br />然后从后向前替换空格,也就是双指针法,过程如下:<br />i指向新长度的末尾,j指向旧长度的末尾。<br /><br />有同学问了,为什么要从后向前填充,从前向后填充不行么?<br />从前向后填充就是$O(n^2)$的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。<br />**其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。**<br />这么做有两个好处:1. 不用申请新数组。1. 从后向前填充元素,避免了从前先后填充元素要来的 每次添加元素都要将添加元素之后的所有元素向后移动。```csharp// 法二: 先扩容 再双指针public String replaceSpace2(String s) {char[] chs = s.toCharArray();int count = 0; // 统计空格个数int oldLen = chs.length;for (char ch : chs) {if (ch == ' ') {count++;}}// 扩容int newLen = oldLen + count * 2;char[] str = new char[newLen];for (int i = 0; i < oldLen; i++) {str[i] = chs[i];}for (int i = newLen - 1, j = oldLen - 1; j < i; i--, j--) {if (str[j] != ' ') {str[i] = str[j];} else {str[i] = '0';str[i - 1] = '2';str[i - 2] = '%';i -= 2;}}return new String(str);}
151. 翻转字符串里的单词
https://leetcode-cn.com/problems/reverse-words-in-a-string/
这道题目可以说是综合考察了字符串的多种操作。
本题的难度:不要使用辅助空间,空间复杂度要求为$O(1)$。
不能使用辅助空间之后,那么只能在原字符串上下功夫了。
想一下,我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。
所以解题思路如下:
- 移除多余空格
- 将整个字符串反转
- 将每个单词反转
举个例子,源字符串为:”the sky is blue “
- 移除多余空格 : “the sky is blue”
- 字符串反转:”eulb si yks eht”
- 单词反转:”blue is sky the”
这样我们就完成了翻转字符串里的单词。
public String reverseWords(String s) {if (s == null || s.length() == 0) {return null;}if (s.equals(" ")) {return " ";}char[] str = s.toCharArray();int len = removeExtraSpaces(str);reverse(str, 0, len - 1);for (int i = 0; i < len; ) {int j = i;while (j < len && str[j] != ' ') {j++;}reverse(str, i, j - 1);i = j + 1;}return new String(str, 0, len);}// 反转字符串s中左闭又闭的区间[start, end]public void reverse(char[] s, int start, int end) {int L = start;int R = end;while (L < R) {char tmp = s[L];s[L++] = s[R];s[R--] = tmp;}}// 移除冗余空格: 使用双指针 (快慢指针)// 返回对应的有效长度public int removeExtraSpaces(char[] str) {int N = str.length;int slow = 0;int fast = 0;// 去掉字符串前面的空格while (fast < N && str[fast] == ' ') {fast++;}for (; fast < N; fast++) {// 去掉字符串中间部分的冗余空格if (str[fast] == ' ' && str[fast] == str[fast - 1]) {continue;} else {str[slow++] = str[fast];}}if (str[slow - 1] == ' ') {return slow - 1;} else {return slow;}}
剑指Offer 58 - II. 左旋转字符串
不能申请额外空间,只能在本串上操作。
不能使用额外空间的话,模拟在本串操作要实现左旋转字符串的功能还是有点困难的。
这道题目依然可以通过局部反转+整体反转 达到左旋转的目的。
具体步骤为:
- 反转区间为前n的子串
- 反转区间为n到末尾的子串
- 反转整个字符串
最后就可以得到左旋n的目的,而不用定义新的字符串,完全在本串上操作。
例如 :示例1中 输入:字符串abcdefg,n=

//额外空间复杂度O(1)public String reverseLeftWords2(String s, int n) {int len = s.length();StringBuilder sb = new StringBuilder(s);// 1. 反转区间为前n的子串reverse(sb, 0, n - 1);// 2. 反转区间为n到末尾的子串reverse(sb, n, len - 1);// 3. 反转整个字符串reverse(sb, 0, len - 1);return sb.toString();}public void reverse(StringBuilder sb, int start, int end) {while (start < end) {char tmp = sb.charAt(start);sb.setCharAt(start++, sb.charAt(end));sb.setCharAt(end--, tmp);}}
6. 实现strStr() (KMP)
https://leetcode-cn.com/problems/implement-strstr/
- kmp算法见如下链接
https://www.yuque.com/docs/share/c0e96027-62d0-411b-abc1-c3e053609458?# 《27.⭐实现strStr() (KMP算法)》
// KMP算法public int strStr(String haystack, String needle) {if (haystack == null || needle == null || needle.length() > haystack.length()) {return -1;}if (needle.length() == 0) {return 0;}char[] str = haystack.toCharArray();char[] match = needle.toCharArray();int[] next = getNextArray(match);int x = 0;int y = 0;while (x < str.length && y < match.length) {if (str[x] == match[y]) {x++;y++;} else if (next[y] == -1) { // y跳到0位置了x++;} else {y = next[y];}}/* y不越界, x越界, -1* y越界, x不越界* y越界, x越界* */return y == match.length ? x - y : -1;}private int[] getNextArray(char[] match) {if (match.length == 1) {return new int[]{-1};}int[] next = new int[match.length];next[0] = -1;next[1] = 0;int i = 2;int cn = 0;while (i < match.length) {if (match[cn] == match[i - 1]) {next[i++] = ++cn;} else if (cn > 0) {cn = next[cn];} else {next[i++] = 0;}}return next;}
459. 重复的子字符串
- 同样用到kmp
https://leetcode-cn.com/problems/repeated-substring-pattern/
如果len % (len - (next[len - 1] + 1)) == 0 ,则说明 (数组长度-最长相等前后缀的长度) 正好可以被 数组的长度整除,说明有该字符串有重复的子字符串
数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。
强烈建议大家把next数组打印出来,看看next数组里的规律,有助于理解KMP算法
public static boolean repeatedSubstringPattern(String s) {int len = s.length();char[] str = s.toCharArray();int[] next = getNextArray(str);int i = next[len - 1];if (i < 0) {return false;} else {if (str[i] == str[len - 1] && len % (len - (next[len - 1] + 1)) == 0) {return true;}}return false;}private static int[] getNextArray(char[] match) {if (match.length == 1) {return new int[]{-1};}int[] next = new int[match.length];next[0] = -1;next[1] = 0;int i = 2;int cn = 0;while (i < match.length) {if (match[cn] == match[i - 1]) {next[i++] = ++cn;} else if (cn > 0) {cn = next[cn];} else {next[i++] = 0;}}return next;}
