代码随想录—力扣刷题笔记之字符串篇

本文介绍了LeetCode中的三道字符串反转题目,涉及原地反转、区间反转和单词反转,以及一道数字替换和字符串右旋题目,展示了使用双指针等技巧解决此类问题的方法。
摘要由CSDN通过智能技术生成

题1 344.反转字符串

344. 反转字符串 - 力扣(LeetCode)

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例 1:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:

输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

代码随想录算法公开课:

字符串基础操作! | LeetCode:344.反转字符串_哔哩哔哩_bilibili

解题思路:

虽然各个编程语言中都有相应的库函数reverse来实现反转字符串,但如果在这道题中采用这种解法,那么这道题就失去了意义。

但也不是所有的题目都不能直接使用库函数。如果题目的关键部分直接可以用库函数解决的话,那建议不要使用库函数。

但是如果使用库函数解决的只是题目中的一环,并且你熟悉这个库函数的作用以及原理的话,那就可以使用。

那么这道题应该怎么解决呢?在链表篇中,有道题教反转链表(力扣206题),在那道题中使用了双指针的方法。那么同样的,双指针的方法也可以在这道题中使用,甚至用法更简单。

因为字符串也是一种数组,所以元素在内存中也是连续分布的,这就决定了反转链表和反转字符串方式上还是有所差异的。

对于字符串,我们就定义两个指针(或者说是索引下标),一个从字符串前面往后扫描,一个从字符串后面往前扫描,两个指针同时向中间移动,在移动的过程中实现元素交换。

本题的解题代码如下所示:

Python:

class Solution(object):
    def reverseString(self, s):
        i = 0
        j = len(s) - 1

        while(i < j):
            s[i],s[j] = s[j],s[i]
            i += 1
            j -= 1
        
        return s 
        """
        :type s: List[str]
        :rtype: None Do not return anything, modify s in-place instead.
        """

Java:

class Solution {
    public void reverseString(char[] s) {
        int l = 0;
        int r = s.length - 1;
        while(l < r){
            char temp = s[l];
            s[l] = s[r];
            s[r] = temp;
            l++;
            r--;
        }
    }
}

Go:

func reverseString(s []byte) {
    left := 0
    right := len(s)-1
    for left < right {
        s[left], s[right] = s[right], s[left]
        left++
        right--
    }
}

题2 541. 反转字符串II

541. 反转字符串 II - 力扣(LeetCode)

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例 1:

输入:s = "abcdefg", k = 2
输出:"bacdfeg"

示例 2:

输入:s = "abcd", k = 2
输出:"bacd"

代码随想录算法公开课:

字符串操作进阶! | LeetCode:541. 反转字符串II_哔哩哔哩_bilibili

解题思路:

这道题的解题思路跟上面那道题是一样的,只是反转的范围不一样。

其实在遍历字符串的过程中,只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。

本题的解题代码如下所示:

Python

class Solution(object):
    def reverseStr(self, s, k):
        def reverse(s):
            left,right = 0,len(s)-1
            while(left < right):
                s[left],s[right] = s[right],s[left]
                left += 1
                right -= 1
            return s  

        result = list(s)

        for i in range(0,len(result),2*k):
            result[i:i+k] = reverse(result[i:i+k])
    
        return ''.join(result)

        

C:

char * reverseStr(char * s, int k){
    int len = strlen(s);

    for (int i = 0; i < len; i += (2 * k)) {
        //判断剩余字符是否少于 k
        k = i + k > len ? len - i : k;

        int left = i;
        int right = i + k - 1;
        while (left < right) {
            char temp = s[left];
            s[left++] = s[right];
            s[right--] = temp;
        }
    }

    return s;
}

Java:

class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        for(int i = 0;i < ch.length;i += 2 * k){
            int start = i;
            // 判断尾数够不够k个来取决end指针的位置
            int end = Math.min(ch.length - 1,start + k - 1);
            while(start < end){
                
                char temp = ch[start];
                ch[start] = ch[end];
                ch[end] = temp;

                start++;
                end--;
            }
        }
        return new String(ch);
    }
}

题3 替换数字

54. 替换数字(第八期模拟笔试) (kamacoder.com)

给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。

例如,对于输入字符串 "a1b2c3",函数应该将其转换为 "anumberbnumbercnumber"。

对于输入字符串 "a5b",函数应该将其转换为 "anumberb"

输入:一个字符串 s,s 仅包含小写字母和数字字符。

输出:打印一个新的字符串,其中每个数字字符都被替换为了number

样例输入:a1b2c3

样例输出:anumberbnumbercnumber

数据范围:1 <= s.length < 10000。

解题思路:

用一个for循环遍历整个字符串,遇到数字就用number替换即可。

本题的解题代码如下所示:

Python:

class Solution:
    def change(self, s):
        lst = list(s) # Python里面的string也是不可改的,所以也是需要额外空间的。空间复杂度:O(n)。
        for i in range(len(lst)):
            if lst[i].isdigit():
                lst[i] = "number"
        return ''.join(lst)

Java:

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String s = in.nextLine();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (Character.isDigit(s.charAt(i))) {
                sb.append("number");
            }else sb.append(s.charAt(i));
        }
        System.out.println(sb);
    }
}

Go:

package main

import "fmt"

func main(){
    var strByte []byte

    fmt.Scanln(&strByte)

    for i := 0; i < len(strByte); i++{
        if strByte[i] <= '9' && strByte[i] >= '0' {
            inserElement := []byte{'n','u','m','b','e','r'}
            strByte = append(strByte[:i], append(inserElement, strByte[i+1:]...)...)
            i = i + len(inserElement) -1
        }
    }

    fmt.Printf(string(strByte))
}

题4 151.翻转字符串里的单词

给定一个字符串,逐个翻转字符串中的每个单词。

示例 1:
输入: "the sky is blue"
输出: "blue is sky the"

示例 2:
输入: "  hello world!  "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:
输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

代码随想录算法公开课:

字符串复杂操作拿捏了! | LeetCode:151.翻转字符串里的单词_哔哩哔哩_bilibili

解题思路:

这道题目可以说是综合考察了字符串的多种操作。

一些同学会使用split库函数,分隔单词,然后定义一个新的string字符串,最后再把单词倒序相加,那么这道题题目就是一道水题了,失去了它的意义。

所以这里我还是提高一下本题的难度:不要使用辅助空间,空间复杂度要求为O(1)。

不能使用辅助空间之后,那么只能在原字符串上下功夫了。

想一下,我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。

所以解题思路如下:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转

举个例子,源字符串为:"the sky is blue "

  • 移除多余空格 : "the sky is blue"
  • 字符串反转:"eulb si yks eht"
  • 单词反转:"blue is sky the"

这样我们就完成了翻转字符串里的单词。

本题的解题代码如下所示:

Python:

class Solution(object):
    def reverseWords(self, s):
        words = s.split()

        left,right = 0,len(words)-1
        while left < right:
            words[left],words[right] = words[right],words[left]
            left += 1
            right -= 1
        
        return ' '.join(words)
        """
        :type s: str
        :rtype: str
        """

Java:

class Solution {
    //用 char[] 来实现 String 的 removeExtraSpaces,reverse 操作
    public String reverseWords(String s) {
        char[] chars = s.toCharArray();
        //1.去除首尾以及中间多余空格
        chars = removeExtraSpaces(chars);
        //2.整个字符串反转
        reverse(chars, 0, chars.length - 1);
        //3.单词反转
        reverseEachWord(chars);
        return new String(chars);
    }

    //1.用 快慢指针 去除首尾以及中间多余空格,可参考数组元素移除的题解
    public char[] removeExtraSpaces(char[] chars) {
        int slow = 0;
        for (int fast = 0; fast < chars.length; fast++) {
            //先用 fast 移除所有空格
            if (chars[fast] != ' ') {
                //在用 slow 加空格。 除第一个单词外,单词末尾要加空格
                if (slow != 0)
                    chars[slow++] = ' ';
                //fast 遇到空格或遍历到字符串末尾,就证明遍历完一个单词了
                while (fast < chars.length && chars[fast] != ' ')
                    chars[slow++] = chars[fast++];
            }
        }
        //相当于 c++ 里的 resize()
        char[] newChars = new char[slow];
        System.arraycopy(chars, 0, newChars, 0, slow); 
        return newChars;
    }

    //双指针实现指定范围内字符串反转,可参考字符串反转题解
    public void reverse(char[] chars, int left, int right) {
        if (right >= chars.length) {
            System.out.println("set a wrong right");
            return;
        }
        while (left < right) {
            chars[left] ^= chars[right];
            chars[right] ^= chars[left];
            chars[left] ^= chars[right];
            left++;
            right--;
        }
    }

    //3.单词反转
    public void reverseEachWord(char[] chars) {
        int start = 0;
        //end <= s.length() 这里的 = ,是为了让 end 永远指向单词末尾后一个位置,这样 reverse 的实参更好设置
        for (int end = 0; end <= chars.length; end++) {
            // end 每次到单词末尾后的空格或串尾,开始反转单词
            if (end == chars.length || chars[end] == ' ') {
                reverse(chars, start, end - 1);
                start = end + 1;
            }
        }
    }
}

Go:

func reverseWords(s string) string {
    b := []byte(s)

    // 移除前面、中间、后面存在的多余空格
    slow := 0
    for i := 0; i < len(b); i++ {
        if b[i] != ' ' {
            if slow != 0 {
                b[slow] = ' '
                slow++
            }
            for i < len(b) && b[i] != ' ' { // 复制逻辑
                b[slow] = b[i]
                slow++
                i++
            }
        }
    }
    b = b[0:slow]
    
    // 翻转整个字符串
    reverse(b)
    // 翻转每个单词
    last := 0
    for i := 0; i <= len(b); i++ {
        if i == len(b) || b[i] == ' ' {
            reverse(b[last:i])
            last = i + 1
        }
    }
    return string(b)
}

func reverse(b []byte) {
    left := 0
    right := len(b) - 1
    for left < right {
        b[left], b[right] = b[right], b[left]
        left++
        right--
    }
}

题5  右旋字符串

字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。

例如,对于输入字符串 "abcdefg" 和整数 2,函数应该将其转换为 "fgabcde"。

输入:输入共包含两行,第一行为一个正整数 k,代表右旋转的位数。第二行为字符串 s,代表需要旋转的字符串。

输出:输出共一行,为进行了右旋转操作后的字符串。

样例输入:

2
abcdefg 

1
2

样例输出:

fgabcde

1

数据范围:1 <= k < 10000, 1 <= s.length < 10000;

解题思路:

本题中,我们需要将字符串右移n位,字符串相当于分成了两个部分,如果n为2,符串相当于分成了两个部分,如图: (length为字符串长度)

右移n位, 就是将第二段放在前面,第一段放在后面,先不考虑里面字符的顺序,是不是整体倒叙不就行了。如图:

此时第一段和第二段的顺序是我们想要的,但里面的字符位置被我们倒叙,那么此时我们在把 第一段和第二段里面的字符再倒叙一把,这样字符顺序不就正确了。 如果:

其实,思路就是 通过 整体倒叙,把两段子串顺序颠倒,两个段子串里的的字符在倒叙一把,负负得正,这样就不影响子串里面字符的顺序了。

本题的解题代码如下所示:

Python:

def reverse(s):
    s_list = list(s)
    left, right = 0, len(s_list) - 1
    while left < right:
        s_list[left], s_list[right] = s_list[right], s_list[left]
        left += 1
        right -= 1
    return ''.join(s_list)

def reverse2(k, s):
    s = list(s)
    n = len(s)
    s[0:n-k] = reverse(s[0:n-k])
    s[n-k:n] = reverse(s[n-k:n])
    s[0:n] = reverse(s[0:n])
    return ''.join(s)

k = int(input())
s = input()
print(reverse2(k, s))

Java:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = Integer.parseInt(in.nextLine());
        String s = in.nextLine();

        int len = s.length();  //获取字符串长度
        char[] chars = s.toCharArray();
        reverseString(chars, 0, len - 1);  //反转整个字符串
        reverseString(chars, 0, n - 1);  //反转前一段字符串,此时的字符串首尾尾是0,n - 1
        reverseString(chars, n, len - 1);  //反转后一段字符串,此时的字符串首尾尾是n,len - 1

        System.out.println(chars);

    }

    public static void reverseString(char[] ch, int start, int end) {
        //异或法反转字符串,参照题目 344.反转字符串的解释
        while (start < end) {
            ch[start] ^= ch[end];
            ch[end] ^= ch[start];
            ch[start] ^= ch[end];
            start++;
            end--;
        }
    }
}

Go:

package main
import "fmt"

func reverse (strByte []byte, l, r int){
    for l < r {
        strByte[l], strByte[r] = strByte[r], strByte[l]
        l++
        r--
    }
}


func main(){
    var str string
    var target int

    fmt.Scanln(&target)
    fmt.Scanln(&str)
    strByte := []byte(str)

    reverse(strByte, 0, len(strByte) - 1)
    reverse(strByte, 0, target - 1)
    reverse(strByte, target, len(strByte) - 1)

    fmt.Printf(string(strByte))
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值