目录
题目链接:151. 反转字符串中的单词 - 力扣(LeetCode)
题目链接:55. 右旋字符串(第八期模拟笔试) (kamacoder.com)
题目链接:28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)
题目链接:459. 重复的子字符串 - 力扣(LeetCode)
Tomorrow is always fresh with no mistakes in it yet.
题目链接:151. 反转字符串中的单词 - 力扣(LeetCode)
要求:不要使用辅助空间,空间复杂度要求为O(1)。在原字符串上进行操作,解题思路如下:
1. 移除前导和尾随空格;
2. 反转整个字符串;
3. 反转每个单词并去除多余空格;
举个例子,源字符串为:"the sky is blue "
- 移除多余空格 : "the sky is blue"
- 字符串反转:"eulb si yks eht"
- 单词反转:"blue is sky the"
这里面有许多小坑需要仔细填,如使用双指针找到单词的开头与结尾,两个单词之间添加空格,设置记录处理后新字符串长度变量的作用等等。
另外如果比较熟悉正则表达式和一些String的API的话,就可以使用StringBuilder偷懒快速解决了。我也在函数reverseWords2也给出了偷懒做法。
class Solution151 {
public String reverseWords(String s) {
char[] chars = s.toCharArray();
int n = chars.length;
// 移除前导和尾随空格
int start = 0, end = n - 1;
while (start <= end && chars[start] == ' ')
start ++;
while (start <= end && chars[end] == ' ')
end --;
if (start > end)
return ""; // 只有空格的情况
// 反转整个字符串
reverse(chars, start, end);
// 反转每个单词并去除多余空格
int i = start, j = start; // 双指针法 定位每个单词
int newLength = start; // 记录处理后的新字符串长度
while (i <= end){
while (i <= end && chars[i] == ' ')
i++; // 找到单词的开始
if (i > end)
break;
// 在两个单词之间添加空格
if (newLength > start)
chars[newLength++] = ' ';
j = i;
while (j <= end && chars[j] != ' ')
j++; // 找到单词的结尾
reverse(chars, i ,j - 1); // 反转单词
while (i < j) {
chars[newLength++] = chars[i++]; // 将反转后的单词复制到新位置
}
}
return new String(chars, start, newLength - start);
}
private void reverse(char[] chars, int left, int right){
while (left < right){
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left ++;
right --;
}
}
// 使用正则表达式和相关函数的偷懒做法
public String reverseWords2(String s) {
s = s.trim(); // 去除首尾空格
String[] words = s.split("\\s+"); // 匹配一个或多个连续的空白字符. 用于分割单词
StringBuilder result = new StringBuilder(); // 创建一个 StringBuilder 来存储结果
// 反向遍历单词数组
for (int i = words.length - 1; i >= 0; i--) {
result.append(words[i]);
if (i != 0){
result.append(" ");
}
}
return result.toString();
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
题目链接:55. 右旋字符串(第八期模拟笔试) (kamacoder.com)
实现字符串右旋转的一个有效方法是①先反转整个字符串,然后②反转前 k
个字符,再③反转剩余的字符。这样我们可以在原地完成旋转操作,而不需要额外的空间。
需要注意如果k大于字符串长度,要取 k 的模,即 k % n。
class KamaCoder151 {
public String rightRotate(String s, int k) {
int n = s.length();
k = k % n; // 如果k大于字符串长度,取 k 的模
char[] chars = s.toCharArray();
// 反转整个字符串
reverse(chars, 0, n - 1);
// 反转前 k 个字符
reverse(chars, 0, k - 1);
// 反转剩余的字符
reverse(chars, k, n - 1);
return new String(chars);
}
// 反转字符数组的子数组
private void reverse(char[] chars, int left, int right) {
while (left < right) {
char temp = chars[left];
chars[left] = chars[right];
chars[right] = temp;
left++;
right--;
}
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
题目链接:28. 找出字符串中第一个匹配项的下标 - 力扣(LeetCode)
要在字符串 haystack 中找到字符串 needle 的第一个匹配项的下标,可以使用多种方法,例如朴素字符串匹配算法或更高效的KMP算法。
KMP算法先留个坑🕳。
Leet用户吐槽:“你不能因为 KMP 是教科书上的算法就把它标为简单题吧😅”
使用朴素字符串匹配算法,直接逐个比较 haystack 中的子字符串和 needle。
class Solution28 {
public int strStr(String haystack, String needle) {
int hLen = haystack.length();
int nLen = needle.length();
if (nLen == 0){
return 0;
}
for (int i = 0; i <= hLen - nLen; i++) {
int j = 0;
while (j < nLen && haystack.charAt(i + j) == needle.charAt(j))
j ++;
if (j == nLen){
return i; // 找到匹配项
}
}
return -1;
}
}
题目链接:459. 重复的子字符串 - 力扣(LeetCode)
🕳++;
-----------------------------------------------------------------------------------------------------------------
提供一个不容易想到的思路:
- 将字符串
s
与自身连接,得到新字符串s' = s + s;
- 去掉
s'
的第一个字符和最后一个字符,得到新字符串t;
- 如果在
t
中能找到s
,说明s
是由其某个子串重复多次构成的。
这种方法的原理在于,若 s
可以由某个子串 t
重复多次构成,则在 s + s
中会包含 s
的重复模式。思考❓
class Solution459 {
public boolean repeatedSubstringPattern(String s) {
String doubled = s + s;
String trimmed = doubled.substring(1,doubled.length() - 1);
return trimmed.contains(s);
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(n)