Offer58-II.左旋转字符串
使用中间数组很容易
public String reverseLeftWords(String s, int n) {
if (n == 0 || s == null || s.length() == 0) {
return s;
}
// 使用中间数组
char[] tempChars = new char[n];
char[] chars = s.toCharArray();
for (int i = 0; i < chars.length; i++) {
// 将 n 位前的字符放入中间数组
if (i < n) {
tempChars[i] = chars[i];
continue;
}
// 将 n 位及之后的字符前移 n 位
chars[i - n] = chars[i];
}
for (int i = 0; i < n; i++) {
// 将中间数组的字符放回原数组
chars[chars.length - n + i] = tempChars[i];
}
return String.valueOf(chars);
}
时间复杂度:O(n)
空间复杂度:O(n)
如果追求降低空间复杂度,进行原地移动,则需要三步:
- 反转 [0, n) 位的字符
- 反转 [n, len) 位的字符
- 反转整个字符
Carl 的题解:
public String reverseLeftWords(String s, int n) {
char[] chars = s.toCharArray();
reverse(chars, 0, chars.length - 1);
reverse(chars, 0, chars.length - 1 - n);
reverse(chars, chars.length - n, chars.length - 1);
return new String(chars);
}
public void reverse(char[] chars, int left, int right) {
while (left < right) {
chars[left] ^= chars[right];
chars[right] ^= chars[left];
chars[left] ^= chars[right];
left++;
right--;
}
}
时间复杂度:O(n)
空间复杂度:O(1)
补充:通过三次异或操作 ^= 可以交换两个变量的值,这样可以省略中间变量,但是当两个变量指向同一个对象时,这个操作会有问题,应当谨慎使用
LeetCode28. 实现 strStr()
KMP 经典使用方式
public int strStr(String haystack, String needle) {
if (needle.length() == 0) {
return 0;
}
int[] next = getNext(needle.toCharArray());
int j = -1;
for (int i = 0; i < haystack.length(); i++) {
while (j >= 0 && haystack.charAt(i) != needle.charAt(j + 1)) {
j = next[j];
}
if (haystack.charAt(i) == needle.charAt(j + 1)) {
j++;
}
if (j == needle.length() - 1) {
return (i - needle.length() + 1);
}
}
return -1;
}
private int[] getNext(char[] needleChars) {
// 生成 next 数组
int[] next = new int[needleChars.length];
// 1. 初始化
// j 指向前缀的尾字符
int j = -1;
next[0] = j;
// i 指向后缀的尾字符,从 1 开始
for (int i = 1; i < needleChars.length; i++) {
// 2. 处理前后缀不相同的情况
while (j >= 0 && needleChars[i] != needleChars[j + 1]) {
j = next[j];
}
// 3. 处理前后缀相同的情况
if (needleChars[i] == needleChars[j + 1]) {
j++;
}
next[i] = j;
}
return next;
}
时间复杂度: O(n + m)
空间复杂度: O(m)
KMP 算法另述
LeetCode459.重复的子字符串
看了 Carl 哥的题解才知道,这是道数学题
Carl 题解:
public boolean repeatedSubstringPattern(String s) {
if (s.equals("")) return false;
int len = s.length();
// 原串加个空格(哨兵),使下标从1开始,这样j从0开始,也不用初始化了
s = " " + s;
char[] chars = s.toCharArray();
int[] next = new int[len + 1];
// 构造 next 数组过程,j从0开始(空格),i从2开始
for (int i = 2, j = 0; i <= len; i++) {
// 匹配不成功,j回到前一位置 next 数组所对应的值
while (j > 0 && chars[i] != chars[j + 1]) j = next[j];
// 匹配成功,j往后移
if (chars[i] == chars[j + 1]) j++;
// 更新 next 数组的值
next[i] = j;
}
// 最后判断是否是重复的子字符串,这里 next[len] 即代表next数组末尾的值
if (next[len] > 0 && len % (len - next[len]) == 0) {
return true;
}
return false;
}
时间复杂度: O(n)
空间复杂度: O(n)