字符串
1. 理论基础
字符串概念:字符串是一种由字符组成的序列,它是一种不可变的数据类型,这意味着一旦创建了一个字符串,你就不能更改其中的字符。
创建字符串:
# 使用单引号
string1 = 'Hello, World!'
# 使用双引号
string2 = "This is a string in double quotes."
# 使用三引号表示多行字符串
string3 = """This is a
multiline string."""
索引和切片:
# 访问特定索引的字符
print(string1[0]) # 输出 'H'
# 使用负索引
print(string1[-1]) # 输出 '!'
# 字符串切片
print(string1[1:5]) # 输出 'ello'
print(string1[:5]) # 从开始到索引5(不包括5)
print(string1[7:]) # 从索引7到末尾
修改字符串:
# 尝试修改字符串中的字符会引发 TypeError
try:
string1[0] = 'h'
except TypeError as e:
print(e) # 输出错误信息
常见字符串方法:
# 大小写转换
print(string1.upper()) # 输出 'HELLO, WORLD!'
print(string2.lower()) # 输出 'this is a string in double quotes.'
# 去除空白字符
print(string3.strip()) # 输出 'This is a\nmultiline string.'
# 字符串查找
print(string1.find('World')) # 输出 7,表示 'World' 开始的索引
print(string1.find('Python')) # 输出 -1,表示未找到
# 字符串替换
print(string1.replace('World', 'Python')) # 输出 'Hello, Python!'
字符串链接:
# 使用 + 操作符
print('Hello, ' + 'World!')
# 使用 join() 方法
print(' '.join(['Hello', 'World']))
字符串迭代:
# 遍历字符串中的每个字符
for char in string1:
print(char)
连接成单一字符串:‘’.join()
join() 是字符串的内置方法,它接受一个可迭代对象作为参数。方法的工作原理是将可迭代对象中的每个元素按照它们在可迭代对象中的顺序连接起来。空字符串 ‘’ 作为连接符,意味着元素之间不会有任何额外的字符插入。
# 将字符列表转换为字符串
s = ['h', 'e', 'l', 'l', 'o']
result = ''.join(s)
print(result) # 输出: hello
2. 相关题目
344.反转字符串
题目描述: 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
思路: 在python中可以直接使用切片操作来反转列表s[:] = s[::-1]
。从更底层的原理实现,可以参考反转链表,使用双指针法。
class Solution:
def reverseString(self, s) -> None:
left, right = 0, len(s) - 1
while left < right:
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
# 读取一行字符串作为输入
input_string = input()
# 将输入的字符串转换为字符列表
s = list(input_string)
# 创建 Solution 实例并调用 reverseString 方法
sol = Solution()
sol.reverseString(s)
# 打印反转后的字符串列表
print(''.join(s))
541. 反转字符串II
题目描述: 给定一个字符串 s 和一个整数 k,从字符串开头算起, 每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符。如果剩余字符少于 k 个,则将剩余字符全部反转。如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
思路: 在上一题的基础上,增加一个for循环,Python 的列表切片会安全地处理索引超过数组的情况,不会引发索引超出数组的错误
class Solution:
def reverseStr(self, s: str, k: int) -> str:
def reverseSubStr(s_sub) -> str:
left,right = 0,len(s_sub)-1
while left<right:
s_sub[left],s_sub[right] = s_sub[right],s_sub[left]
left+=1
right-=1
return s_sub
ls = list(s) # 将字符串转换为字符列表
for cur in range(0,len(s),2*k): # 步长为2*k
ls[cur:cur+k] = reverseSubStr(ls[cur:cur+k]) # 这时 Python 的列表切片会安全地处理这种情况,不会引发索引超出数组的错误
return ''.join(ls)
s = input().strip()
k = int(input().strip())
# 创建solution实例并调用方法
sol = Solution()
result = sol.reverseStr(s,k)
print(result)
替换数字
题目描述: 给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。例如,对于输入字符串 “a1b2c3”,函数应该将其转换为 “anumberbnumbercnumber”。对于输入字符串 “a5b”,函数应该将其转换为 “anumberb”
思路: 采用python中列表list内置的isdigit()
来判断第i个元素是否为数字,采用lst[i]
引用第i个元素。
class Solution:
def change(self, s):
lst = list(s) # 将字符串转换为字符列表
for i in range(len(lst)):
if lst[i].isdigit():
lst[i] = 'number'
return ''.join(lst)
# 从标准输入中读取字符串
s = input().strip() # 去除空格
# 创建solution实例并调用change方法
sol = Solution()
result = sol.change(s)
# 将结果输出到标准输出
print(result)
151.翻转字符串里的单词
题目描述: 给定一个字符串,逐个翻转字符串中的每个单词。
思路: 采用python中列表list内置的方法split()
来分割原有的字符串,并返回一个包含这些单词的列表。例如,如果 s 是 “hello world”,那么 words 将是 [‘hello’, ‘world’]。
class Solution:
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)
# 读取一行字符串作为输入
s = input().strip() # 用于溢出用户输入字符串两端的空白字符
# 创建 Solution 实例并调用 reverseWords 方法
sol = Solution()
result = sol.reverseWords(s)
# 打印反转后的字符串列表
print(result)
右旋字符串
题目描述: 字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。
思路: 在python中可以使用切片实现,值得注意的是,由于字符串本身是不可变的,很难完全避免创造新对象。
方法一: 直接创造新的字符串对象
# 右旋字符串
def right_rotate_string(s, k):
"""
将字符串 s 右旋 k 个字符。
参数:
s (str): 输入的字符串。
k (int): 需要旋转的字符数。
返回:
str: 右旋后的字符串。
"""
# 通过切片反转第一段和第二段字符串
# 注意:Python 中字符串是不可变的,所以需要创建新的字符串
return s[-k:] + s[:-k]
# 获取输入的数字 k 和字符串 s
k = int(input("请输入要旋转的字符数 k: "))
s = input("请输入字符串 s: ")
# 调用函数并打印结果
rotated_s = right_rotate_string(s, k)
print("旋转后的字符串为:", rotated_s)
方法二:如果想避免创造一个新的字符串对象,则将原有的字符串先转换成一个字符串列表
def right_rotate_string_inplace(s, k):
"""
在原字符串上实现右旋操作,通过将字符串转换为列表来实现可变性。
参数:
s (str): 输入的字符串。
k (int): 需要旋转的字符数。
返回:
str: 右旋后的字符串。
"""
# 将字符串转换为列表
s_list = list(s)
# 计算实际需要旋转的字符数
k = k % len(s_list)
# 右旋操作
s_list = s_list[-k:] + s_list[:-k]
# 将列表转换回字符串
return ''.join(s_list)
# 获取输入的数字 k 和字符串 s
k = int(input("请输入要旋转的字符数 k: "))
s = input("请输入字符串 s: ")
# 调用函数并打印结果
rotated_s = right_rotate_string_inplace(s, k)
print("旋转后的字符串为:", rotated_s)
28. 实现 strStr()
题目描述: 实现 strStr() 函数。给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
思路: 采用KMP的思想:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。
KMP概念: KMP是一种高效的字符串搜索/模式匹配算法。核心思想是当字符串不匹配时,能够利用已经部分匹配的信息,避免从头开始匹配,从而提高搜索效率。算法关键点如下:
- 前后缀:字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
- 最长公共前后缀:在这里理解为最长相同前后缀的长度。字符串aa的最长相等前后缀为1。 字符串aaa的最长相等前后缀为2。 等等。
- 前缀函数(部分匹配表):用于记录模式字符串(pattern)中每个位置之前的子字符串的最长相同前缀和后缀的长度。用来回退,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配,即要看它的前一个字符的前缀表的数值是多少。为什么要前一个字符的前缀表的数值呢,因为要找前面字符串的最长相同的前缀和后缀。前一个字符的前缀表的数值是2, 所以把下标移动到下标2的位置继续比配。一般常用一个next数组,来作为前缀表。
- 复杂度:KMP 算法的时间复杂度是 O(n + m),其中n为文本串长度,m为模式串长度。暴力的解法显而易见是O(n × m)。
class Solution:
def getNext(self, next, s):
# 用于生成模式字符串的 next 数组
j = -1 # j 表示模式字符串已经成功匹配的部分的末尾索引
next[0] = j # 初始化 next 数组的第一个元素为 -1
for i in range(1, len(s)): # 从索引 1 开始遍历模式字符串
while j >= 0 and s[i] != s[j+1]: # 如果当前字符不匹配
j = next[j] # 通过 j = next[j] 进行回退
if s[i] == s[j+1]: # 如果当前字符匹配
j += 1 # 移动 j 到下一个位置
next[i] = j # 更新 next 数组
def strStr(self, haystack: str, needle: str) -> int:
if not needle: # 如果模式字符串为空,返回 0
return 0
next = [0] * len(needle) # 初始化 next 数组
self.getNext(next, needle) # 计算 next 数组
j = -1 # 由于 j 被初始化为 -1,后续比较时要比较 j + 1
for i in range(len(haystack)): # i 从 0 开始遍历文本字符串
while j >= 0 and haystack[i] != needle[j+1]: # 发生不匹配
j = next[j] # 根据 next 数组回退 j
if haystack[i] == needle[j+1]: # 如果当前字符匹配
j += 1 # 移动 j 到下一个位置
if j == len(needle) - 1: # 如果匹配了整个模式字符串
return i - len(needle) + 1 # 返回匹配的起始索引
return -1 # 如果遍历完文本字符串都没有找到匹配,则返回 -1
# 从标准输入读取文本字符串和模式字符串
haystack = input().strip()
needle = input().strip()
# 创建 Solution 实例并调用 strStr 方法
sol = Solution()
result = sol.strStr(haystack, needle)
# 输出匹配的起始索引,如果没有匹配则输出 -1
print(result)