代码随想录算法训练营第八天|字符串专题一
今日任务:
-
leetcode 344.反转字符串
-
leetcode 541. 反转字符串II
-
leetcode 剑指Offer 05.替换空格
-
leetcode 151.翻转字符串里的单词
-
leetcode 剑指Offer58-II.左旋转字符串
今天主要是以字符串反转为主!
leetcode 344.反转字符串
题目链接:leetcode 344.反转字符串
题目详情:
思路
这题的解题思路还是比较简单的,与反转数组是一致的,都是使用双指针,left从0开始,right从length-1开始,两两交换,交换后left和right同时移动,直到两者相遇。
动图:
代码
class Solution {
public void reverseString(char[] s) {
int left=0;
int right=s.length-1;
while(left<=right){
char temp=s[left];
s[left]=s[right];
s[right]=temp;
left++;
right--;
}
}
}
leetcode 541. 反转字符串II
题目详情:
思路
本题与数组专题中的螺旋矩阵类似,都是一种行为模拟题,那么我们可以怎么进行模拟呢?
从0开始遍历字符串,为了方便操作我们先把字符串转换为数组,然后把数组转换为字符串返回即可。那么我们从0开始遍历时判断剩余字符的个数,如果size<k,则把后面部分全部反转,如果size<2*k&&szie>=k,那么就反转k个然后返回结果。如果i是>=2k的话,那么就是还是反转前k个,然后i=i+2×k,继续进行上述判断。
代码
class Solution {
public String reverseStr(String s, int k) {
char[] chs =s.toCharArray();
for(int i=0;i<chs.length;i+=2*k){
//判断剩余字符个数
int size=chs.length-i;
if(size<k){
return new String(reverse(chs,i,(i+size-1)));
}
if(size>=k && size<2*k){
return new String(reverse(chs,i,(i+k-1)));
}
chs=reverse(chs,i,(i+k-1));
}
return new String(chs);
}
public char[] reverse(char[] chs,int start,int end){
while(start<=end){
char temp= chs[start];
chs[start]=chs[end];
chs[end]=temp;
start++;
end--;
}
return chs;
}
}
leetcode 剑指Offer 05.替换空格
题目详情:
思路
这个题就比较简单了,就使用StringBuilder,遍历字符串,碰到空格就添加"%20"。
进阶
但是如果我们不使用额外空间应该怎么操作呢,因为java中String是不可变的,所以使用Java实现是不太现实的,但是在c++中,可以对字符串进行扩容,那么我们可以遍历一遍字符串,找到空格的数量,然后根据空格数量对字符串进行扩容,之后使用双指针进行填充就好了。这里选择从后往前填充。
动图演示:
方法一:Java实现(使用额外空间O(n))
class Solution {
public String replaceSpace(String s) {
StringBuilder sb =new StringBuilder();
for(int i=0;i<s.length();i++){
char ch=s.charAt(i);
if(" ".equals(String.valueOf(ch))){
sb.append("%20");
}else{
sb.append(ch);
}
}
return sb.toString();
}
}
方法二:C++实现(不使用额外空间,空间复杂度O(1))
class Solution {
public:
string replaceSpace(string s) {
int count = 0; // 统计空格的个数
int sOldSize = s.size();
for (int i = 0; i < s.size(); i++) {
if (s[i] == ' ') {
count++;
}
}
// 扩充字符串s的大小,也就是每个空格替换成"%20"之后的大小
s.resize(s.size() + count * 2);
int sNewSize = s.size();
// 从后向前将空格替换为"%20"
//使用双指针填充
for (int i = sNewSize - 1, j = sOldSize - 1; j < i; i--, j--) {
if (s[j] != ' ') {
s[i] = s[j];
} else {
s[i] = '0';
s[i - 1] = '2';
s[i - 2] = '%';
i -= 2;
}
}
return s;
}
};
leetcode 151.翻转字符串里的单词
题目详情:
思路
我们的解题要求是不使用额外空间,也就是在原串上进行操作。
区间反转+全部反转
具体解题思路就是,先进行移除多余的空格,然后就是对每个单词进行反转,之后对整个字符串进行反转,最后就能得到最终的结果。那么为什么使用区间反转+全部反转就能做到呢?
如果说一个字符串是可以进行分组的,也就是可以分为几个部分,如果结果要求我们将组进行反转,且组内的字符顺序不变的话就可以使用区间+全部反转了。当你使用区间反转后,分组内的字符全部都是反转了的 ,而当你在进行全部反转,那么组内的字符顺序将全部变成原来的顺序了,并且该分组也进行了交换。
举例:
原字符串:the sky is blue
移除空格:the sky is blue
区间反转:eht yks si eulb
全部反转:blue is sky the
这样应该清晰很多了吧。
那么我们什么时候可以使用区间+全部反转的方式呢?
字符串的区间+全部反转在我的理解中是,如果字符串中存在分组,其反转是按组进行反转的话,那么就可以使用区间反转+全部反转,保证组内顺序不变,组顺序反转。
代码
class Solution {
/**
* 不使用Java内置方法实现
*
* 1.去除首尾以及中间多余空格
* 2.反转各个单词
* 3.反转各个字符串
*/
public String reverseWords(String s) {
// 1.去除首尾以及中间多余空格
StringBuilder sb = removeSpace(s);
// 2.反转各个单词
reverseEachWord(sb);
// 3.反转各个字符串
reverseString(sb, 0, sb.length() - 1);
return sb.toString();
}
//移除多余空格api,使用双指针填充
private StringBuilder removeSpace(String s) {
int start = 0;
int end = s.length() - 1;
while (s.charAt(start) == ' ') start++;
while (s.charAt(end) == ' ') end--;
StringBuilder sb = new StringBuilder();
while (start <= end) {
char c = s.charAt(start);
if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {
sb.append(c);
}
start++;
}
return sb;
}
//找出每个单词,并且调用reverseString(StringBuilder sb, int start, int end)方法反转每个单词
private void reverseEachWord(StringBuilder sb) {
int start = 0;
int end = 1;
int n = sb.length();
while (start < n) {
while (end < n && sb.charAt(end) != ' ') {
end++;
}
reverseString(sb, start, end - 1);
start = end + 1;
end = start + 1;
}
}
/**
* 反转字符串指定区间[start, end]的字符
*/
public void reverseString(StringBuilder sb, int start, int end) {
while (start < end) {
char temp = sb.charAt(start);
sb.setCharAt(start, sb.charAt(end));
sb.setCharAt(end, temp);
start++;
end--;
}
}
}
leetcode 剑指Offer58-II.左旋转字符串
题目链接:leetcode 剑指Offer58-II.左旋转字符串
题目详情:
思路
这里我们使用区间反转+全部反转,也就说我们可以先反转左区间[0,k-1]中的字符,然后反转右区间[k,nums.length()-1]中的字符,反转整个字符串,就能得到最终结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xxnjO4O1-1664377341611)(%E5%AD%97%E7%AC%A6%E4%B8%B2-09.28.assets/image-20220928223350848.png)]
代码
class Solution {
public String reverseLeftWords(String s, int n) {
//区间+全部翻转
//区间为0-n,n+1 - s.length()-1
//字符串分为了两组,且是按组进行翻转,组内的内容顺序不变,组进行了翻转
char[] chs=s.toCharArray();
int left=0;
int right=n-1;
while(left<=right){
char temp =chs[left];
chs[left]=chs[right];
chs[right]=temp;
left++;
right--;
}
left=n;
right=chs.length-1;
while(left<=right){
char temp =chs[left];
chs[left]=chs[right];
chs[right]=temp;
left++;
right--;
}
left=0;
right=chs.length-1;
while(left<=right){
char temp =chs[left];
chs[left]=chs[right];
chs[right]=temp;
left++;
right--;
}
return new String(chs);
}
}