LeetCode刷题小记 四、【字符串】

1.字符串

写在前面

本系列笔记主要作为笔者刷题的题解,所用的语言为Python3,若于您有助,不胜荣幸。

字符串类题目的思路在算法性的思路上和数组类题目的思路类似,具体需要注意的就是,字符串在不同编程语言中的特性,合理利用在不同语言中的特性有效完成题目的要求是十分重要的。

1.1 反转字符串

344. 反转字符串

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

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

解法一:双指针法

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        left: int = 0
        right: int = len(s) - 1
        while left < right:
            s[left], s[right] = s[right], s[left]       # python的特性,使得我们可以不需要中间变量temp就可以交换两个元素的值
            left += 1
            right -= 1

解法二:使用栈

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        stack: List = []
        for char in s:
            stack.append(char)
        for i in range(len(s)):
            s[i] = stack.pop()

1.2 反转字符串II

541. 反转字符串 II

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

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

双指针法

class Solution:
    def reverseChar(self, s: List[str]) -> List[str]:
        left: int = 0
        right: int = len(s) - 1
        while left < right:
            s[left], s[right] = s[right], s[left]
            left += 1
            right -= 1
        return s

    def reverseStr(self, s: str, k: int) -> str:
        res: List[str] = list(s)
        for cur in range(0, len(s), 2*k):
            res[cur:cur+k] = self.reverseChar(res[cur:cur+k])       # python中切片长度超过可迭代对象的长度,会默认将切片的长度限制到可迭代对象的长度内
        return ''.join(res)     # 使用''作为连接符,连接res中的所有元素

1.3 替换数字

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

给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。 例如,对于输入字符串 “a1b2c3”,函数应该将其转换为 “anumberbnumbercnumber”。

class Solution:
    def replaceNumber(self,s:str)->str:
        res: List[str] = list(s)
        for i in range(len(res)):
            if res[i] in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']:
                res[i] = 'number'
        return ''.join(res)
class Solution:
    def replaceNumber(self,s:str)->str:
        res: List[str] = list(s)
        for i in range(len(res)):
            if res[i].isdigit():	# str的isdigit()方法可以判断字符串是否为数字
                res[i] = 'number'
        return ''.join(res)

1.4反转字符串中的单词

151. 反转字符串中的单词

解法一:双指针

class Solution:
    def reverseWords(self, s: str) -> str:
        res: List[str] = [word.strip() for word in s.split()]   # 分割单词且除去多余空格
        left: int = 0
        right: int = len(res) - 1
        while left < right:
            res[left], res[right] = res[right], res[left]
            left += 1
            right -= 1
        return ' '.join(res)

解法二:pythonic

def reverseWords(self, s: str) -> str:
        res: List[str] = [word.strip() for word in s.split()]   # 分割单词且除去多余空格

        return ' '.join(res[::-1])

解法三:使用快慢指针手搓removeSpaces

class Solution:
    def removeSpaces(self, s: str) -> List[str]:
        res: List[str] = []
        slowIndex: int = 0
        fastIndex: int = 0
        
        while fastIndex < len(s) and s[fastIndex] == " ": # 移除开头的空格
            fastIndex += 1
        while fastIndex < len(s):
            if s[fastIndex] != ' ':
                if slowIndex != 0 and res[slowIndex - 1] == ' ':
                    res.append('')  # 在单词之间添加空格
                    slowIndex += 1
                wordStart: int = fastIndex
                while fastIndex < len(s) and s[fastIndex] != ' ':  # 寻找单词结束位置
                    fastIndex += 1
                res.append(s[wordStart:fastIndex])  # 保存单词
                slowIndex += 1
            else:
                fastIndex += 1
        return res

    def reverseWords(self, s: str) -> str:
        res: List[str] = self.removeSpaces(s)
        left: int = 0
        right: int = len(res) - 1
        while left < right:
            res[left], res[right] = res[right], res[left]
            left += 1
            right -= 1
        return ' '.join(res)

1.5右旋字符串

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

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

class Solution:
    def rotateStr(self,s:str,n:int)->str:
        res: List[str] = list(s)
        for _ in range(n):
            res.insert(0, res.pop())
        return ''.join(res)

1.6KMP算法

KMP算法中关键的点是前缀表,前缀表的作用是用来回退的,它记录了在模式串和文本串不匹配的时候,模式串应该从哪里开始重新匹配。

还有一个重要的概念是最长公共前后缀,字符串的前缀指的是不包含最后一个字符的所有以第一个字符开头的连续子串。后缀指的是不包含第一个字符的所有以最后一个字符结尾的连续子串。我们通常用next数组来表示前缀表。

28. 找出字符串中第一个匹配项的下标

给你两个字符串 haystackneedle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1

解法一:KMP法

在KMP解法中,我们主要包含以下的步骤

  • 初始化
  • 生成前缀表next
  • 遍历字符串,并且使用前缀表next来进行回溯

我们需要先生成一个前缀表用来回溯,那如何生成前缀表呢?我们需要一个前缀末尾指针j和一个后缀末尾指针i来帮助我们完成前缀表的生成,我们通过对比前缀末尾指针j和后缀末尾指针i是否相同来获得整个前缀表

class Solution:
    def getNext(self, s:str) -> List[int]:
        """
        获取前缀表
        j: 前缀末尾
        i: 后缀末尾
        """
        j: int = 0
        next: List[int] = [0] * len(s) # 初始化前缀表
        for i in range(1, len(s)):      
            while j>0 and s[i] != s[j]: # 对前缀末尾j进行回溯
                j = next[j-1]
            if s[i] == s[j]:
                j += 1
            next[i] = j
        return next


    def strStr(self, haystack: str, needle: str) -> int:
        j: int = 0
        next: List[int] = self.getNext(needle)
        for i in range(len(haystack)):
            while j>0 and haystack[i] != needle[j]:
                j = next[j-1]
            if haystack[i] == needle[j]:
                j += 1
            if j == len(needle):
                return i - len(needle) + 1
        return -1

解法二:暴力法

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        m, n = len(haystack), len(needle)
        for i in range(m):
            if haystack[i:i+n] == needle:
                return i
        return -1

1.7重复的子字符串

459. 重复的子字符串

给定一个非空的字符串 s ,检查是否可以通过由它的一个子串重复多次构成。

解法一:暴力法

class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        n: int = len(s)
        if n <= 1:
            return False
        for i in range(1, n//2+1):  # 索引只需要遍历一半
            if n % i == 0:
                substr = s[:i]
                if substr * (n//i) == s:
                    return True
        return False

解法二:KMP法

class Solution:
    def getNext(self, s: str) -> List[int]:
        j: int = 0  # 前缀指针
        next: List[int] = [0] * len(s)
        for i in range(1, len(s)):  # 后缀指针
            while j>0 and s[i] != s[j]: # 使用前缀表进行回溯
                j = next[j-1]
            if s[i] == s[j]:
                j += 1
            next[i] = j
        return next

    def repeatedSubstringPattern(self, s: str) -> bool:
        next: List[int] = self.getNext(s)
        if next[-1] != 0 and len(s) % (len(s) - next[-1]) == 0:
            return True
        else:
            return False

Reference

[1] Hello 算法
[2] 代码随想录

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值