字符串在 Python 中是不可变数据类型。如果你对字符串进行任何修改,实际上是创建了一个新的字符串对象,而不是修改原来的字符串。
详细解释:
-
不可变性: 字符串的不可变性意味着一旦创建了一个字符串对象,它的内容就不能被修改。任何尝试修改字符串的操作,实际上都是在创建一个新字符串,并返回这个新字符串的引用。
-
创建新副本: 当你对字符串进行“修改”时(例如拼接、切片、替换等操作),Python 实际上会创建一个新的字符串对象,包含修改后的内容,而原来的字符串对象保持不变
1.反转字符串
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
for i in range(len(s)//2):
s[i], s[len(s)-1-i] = s[len(s)-1-i], s[i]
return s
class Solution {
public:
void reverseString(vector<char>& s) {
for(int i =0;i<(s.size()/2);i++){
swap(s[i],s[s.size()-1-i]);
}
}
};
2.1字符串所有组合
牛客acm模式,给出一个字符串,求出所有字符组合
import sys
for line in sys.stdin:
line = line.strip()
arr = [""]
for i in range(len(line)):
current_length = len(arr)
for j in range(current_length):
new_seq = arr[j] + line[i]
if new_seq not in arr:
arr.append(new_seq)
arr.remove("")
arr = sorted(arr, key=lambda x: (len(x), x))
print(" ".join(arr))
2.2
输入一个字符串,输出该字符串中相邻字符的所有组合。
import sys
for line in sys.stdin:
line = line.strip()
res = []
index=0
for i in range(len(line)):
index += 1
arr = set()
for j in range(len(line)-i):
arr.add(line[j:j+index])
arr = list(arr)
arr.sort()
res+=arr
print(" ".join(res))
思路:外层循环的i不断增加index并缩小j遍历次数,并增大每次arr.add的元素的长度
3.反转字符串
class Solution:
def reverseStr(self, s: str, k: int) -> str:
length = len(s)
count = 0
s1=[]
if length<k:
s1+=(s[length-1::-1])
elif length<2*k and length>=k:
s1+=(s[k-1::-1])
s1+=s[k:length]
else:
for _ in range(length):
count += 1
if count%(2*k)==0:
if count==2*k:
s1 += s[count-k-1::-1]
else:
s1 += s[count-k-1:count-2*k-1:-1]
s1+=s[count-k:count]
if length-count <k:
s1+=(s[length-1:count-1:-1])
elif length-count<2*k and length-count>=k:
s1+=(s[count+k-1:count-1:-1])
s1+=s[count+k:length]
return ''.join(s1)
更加python式的写法
要点s[p:p2]即使最后剩余的元素少于k个,根据切片特性 s[p:p2]
的切片操作不会引发越界错误。相反,它会自动截取从索引 p
开始直到字符串末尾的所有字符。
class Solution:
def reverseStr(self, s: str, k: int) -> str:
# Two pointers. Another is inside the loop.
p = 0
while p < len(s):
p2 = p + k
# Written in this could be more pythonic.
s = s[:p] + s[p: p2][::-1] + s[p2:]
p = p + 2 * k
return s
class Solution {
public:
string reverseStr(string s, int k) {
for(int i=0;i<s.size();i+=(2*k)){
if(i+k<=s.size()){
reverse(s.begin()+i,s.begin()+i+k);
}
else{
reverse(s.begin()+i,s.end());
}
}
return s;
}
};
4.替换数字
lst[i].isdigit()
import sys
line = list(input())
for i in range(len(line)):
if ord(line[i])-ord('a')>=0 and ord(line[i])-ord('a')<=25:
pass
else:
line[i] = 'number'
print(''.join(line))
思路:先扩充,然后从后往前遍历一但是数字字符,就将number反向加入
#include <iostream>
using namespace std;
int main() {
string s;
while (cin >> s) {
int sOldIndex = s.size() - 1;
int count = 0; // 统计数字的个数
for (int i = 0; i < s.size(); i++) {
if (s[i] >= '0' && s[i] <= '9') {
count++;
}
}
// 扩充字符串s的大小,也就是将每个数字替换成"number"之后的大小
s.resize(s.size() + count * 5);
int sNewIndex = s.size() - 1;
// 从后往前将数字替换为"number"
while (sOldIndex >= 0) {
if (s[sOldIndex] >= '0' && s[sOldIndex] <= '9') {
s[sNewIndex--] = 'r';
s[sNewIndex--] = 'e';
s[sNewIndex--] = 'b';
s[sNewIndex--] = 'm';
s[sNewIndex--] = 'u';
s[sNewIndex--] = 'n';
} else {
s[sNewIndex--] = s[sOldIndex];
}
sOldIndex--;
}
cout << s << endl;
}
}
C++ 中的字符判断
在 C++ 中,字符类型 char
本质上是一个整数类型,可以直接与其他字符或整数进行比较。每个字符都对应一个 ASCII 码值,像 '0'
到 '9'
的字符的 ASCII 码值是连续的,因此你可以直接使用比较运算符来判断一个字符是否在某个范围内。
if (s[i] >= '0' && s[i] <= '9') {
count++;
}
在上面的代码中,s[i]
是一个字符,它会被自动转换为对应的 ASCII 值,然后与 '0'
和 '9'
的 ASCII 值进行比较。由于 '0'
到 '9'
的 ASCII 值是连续的,这种比较可以有效判断字符是否为数字字符。
Python 中的字符判断
在 Python 中,字符本质上是长度为 1 的字符串对象,字符串的比较依然是基于字符的 Unicode 码点(类似于 ASCII 值)进行的。不过,Python 提供了一些内置函数来处理这些判断任务。
在这种情况下,Python 也会自动将字符与 '0'
和 '9'
的 Unicode 码点进行比较,所以这段代码与 C++ 中的代码是等效的。
然而,如果你想直接访问字符的 Unicode 码点,可以使用 ord()
函数,例如:
if '0' <= s[i] <= '9':
count += 1
if 48 <= ord(s[i]) <= 57:
count += 1
5.反转字符串
class Solution:
def reverseWords(self, s: str) -> str:
s1 = s.split()
for i in range(len(s1)//2):
s1[i], s1[len(s1)-i-1] = s1[len(s1)-i-1], s1[i]
return " ".join(s1)
C++:考虑更多的是去除空格,主要思路是利用快慢指针,快指针先遍历,发现非空并且慢指针不为0说明不是第一个单词了,所以先将慢指针赋一个空,之后再将非空的单词赋给慢指针,这样就实现了单词之间间隔一个空。还有一点很巧妙就是while最后结束时的i和下一次for循环的i差了2,这是因为单词之间要有一个空格所以下一次循环的i指向的是单词结束后的第两位,就是因为单词之间至少会有一个空格,所以跳过这个一定会有的空格,开始判断后面有没有空格。
class Solution {
public:
void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 []
for (int i = start, j = end; i < j; i++, j--) {
swap(s[i], s[j]);
}
}
void removeExtraSpaces(string& s){
int slow = 0;
for(int i=0;i<s.size();i++){
if(s[i]!=' '){
if(slow!=0) s[slow++]=' ';
while(i<s.size()&&s[i]!=' '){
s[slow++] = s[i++];
}
}
}
s.resize(slow);
}
string reverseWords(string s) {
removeExtraSpaces(s);
reverse(s,0,s.size()-1);
int start=0;
for(int i=0;i<=s.size();++i){
if(i==s.size()||s[i]==' '){
reverse(s,start,i-1);
start = i+1;
}
}
return s;
}
};
6.右旋字符串,C++有reverse就很好做,python直接用切片
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
int n;
string s;
cin >> n;
cin >> s;
int len = s.size();
reverse(s.begin(),s.end());
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.end());
cout << s <<endl;
}
import sys
n = int(input())
for line in sys.stdin:
print(line[len(line)-n:]+line[:len(line)-n])
错误代码:没有考虑到回退到哪里。
前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。也就是不用再每次从模式串的头开始匹配了,直接从最长相等前后缀的哪个位置开始继续匹配主串里面的元素,相当于说主串一直遍历,模式串的指针会出现回退。
class Solution {
public:
int strStr(string haystack, string needle) {
int lenA = haystack.size();
int lenB = needle.size();
if(lenB==0){
return 0;
}
int slow=0;
for(int i=0;i<lenA;i++){
if(haystack[i]==needle[slow]){
slow++;
}
else if(haystack[i]!=needle[slow]){
slow=0;
}
if(slow==lenB){
return i-slow+1;
}
}
return -1;
}
};
直接暴力,python用切片比较
class Solution {
public:
int strStr(string haystack, string needle) {
int lenA = haystack.size();
int lenB = needle.size();
if(lenB==0){
return 0;
}
for(int i=0;i<lenA;i++){
if(haystack.substr(i,lenB)==needle){
return i;
}
}
return -1;
}
};
对于KMP重要的理解就是前缀和后缀:下标5之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀 和 后缀字符串是 子字符串aa ,因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面重新匹配就可以了。
就我个人理解:确定了前缀和后缀的最大公共长度,那么一旦遇到不匹配通过查看next前一个的公共长度就可以回退而不是说从新开始。就比如说上面这张图,遇到f不匹配会回退到下标2这个位置,也就是到了b这个位置重新比较。避免了重复比较。