- 字符串简介
- 面试题总体分析
- 一些例题:
- 0-1串交换顺序
- 字符的替换与复制
- 交换星号
- 子串变位词
- 单词/字符串翻转
- 总结
1. 字符串简介
- 尽量转化成字符数组[python中字符串是不可变类型]
- 和数组相关–内容广泛
- 概念相关:字典序
- 简单操作:插入、删除字符、旋转
- 规则判断: 罗马数字转换, 是否是合法的整数,浮点数
- 数字运算:大数加法,二进制加法
- 排序、交换(partition过程)
- 字符统计(hash):变位词
- 匹配(正则匹配[递归实现], 全串匹配, KMP匹配,周期判断)
- 动态规划(LCS【最长公共子序列】, 编辑距离, 最长回文串)
- 搜索(单词变换, 排列组合)
0-1交换
把一个0-1串(只包含0和1的串)进行排序,可以交换任意两个位置,请问最少交换的次数?
test = '00000100001101101'
ans = 0
test_list = list(test)
low = 0
high = len(test_list) - 1
while low < high:
while low < high and test_list[low] == '0':
low += 1
while low < high and test_list[high] == '1':
high -= 1
if low < high:
# 不然会陷入到死循环
low += 1
high -= 1
# test_list[low], test_list[high] = test_list[high], test_list[low]
ans += 1
ans
2. 字符串的替换和复制
- 删除一个字符串里面所有的a,并且复制所有的b。
- 字符数组足够大
test = 'abadcabebfdaba'
print('abadcabebfdaba')
# 先删除a,可以利用原来字符串的空间
test_list = list(test)
n, num_b = 0, 0
for i in range(len(test)):
if test[i] != 'a':
test_list[n] = test[i]
n += 1
if test[i] == 'b':
num_b += 1
# 先计算字符串中有几个b,然后得到复制后的长度
# 复制---倒着复制
need = (n - 1) + num_b - (len(test) - 1 )
if need > 0:
test_list = test_list + [0] * need
else:
# 如果多余的话,就删除掉
for _ in range(need, 0, 1):
test_list.pop(_)
j = n - 1
i = len(test_list)-1
while i >= 0:
test_list[i] = test_list[j]
i -= 1
if test_list[j] == 'b':
# 遇到b的时候,赋值一次,移动两次
test_list[i] = 'b'
i -= 1
j -= 1
ret = ''.join(test_list)
## 如何把字符串中的空格变成20%
test = 'aes df fefs fe fgs'
space_num = 0
for i in test:
if i.isspace():
space_num += 1
# 把字符串转化成字符数组
test_list = list(test) + ['0'] * space_num * 2
# 从后向前复制的基本框架
j = len(test) - 1
i = len(test_list) - 1
while i >= 0:
if test_list[j].isspace():
test_list[i] = '0'
i -= 1
test_list[i] = '2'
i -= 1
test_list[i] = '%'
i -= 1
else:
test_list[i] = test_list[j]
i -= 1
j -= 1
print(''.join(test_list))
交换星号
一个字符串中只包含*和数字请将其*都放在开头
test = '01*8**2*0**1'
test_list = list(test)
n = len(test)
low, high = 0, n - 1
while low < high:
while low < high and test_list[low] == '*':
low += 1
while low < high and test_list[high] != '*':
high -= 1
test_list[low], test_list[high] = test_list[high], test_list[low]
print(''.join(test_list))
# 第一种方法的数字会发生变化,采用循环不变式,数字的位置也发生变化
test = '01*8**2*0**1'
test_list = list(test)
n = len(test)
low, high = 0, 0
while high < n:
if test_list[high] == '*':
test_list[low], test_list[high] = test_list[high], test_list[low]
low += 1
high += 1
print(''.join(test_list))
# 不改变数字的位置,达到相同的效果
# 不改变数字的位置,只能使用这种方法
test = '01*8**2*0**1'
test_list = list(test)
n = len(test)
i, j = n-1, n-1
while j >= 0:
if test_list[j] != '*':
test_list[i] = test_list[j]
i -= 1
j -= 1
for _ in range(i-1, -1, -1):
test_list[_] = '*'
print(''.join(test_list))
子串变位词
- 给定两个串a和b,问b是否是a的子串的变位词。例如输入a=hello,b=lel,lle,ello都是True
, 但是b=elo,是false - 滑动窗口思想
- 动态维护一个“窗口”
- 比如b的长度为3, 我们考察a[0…2], [1…3],[2…4]是否和b是变位词
- 如何与b比较-----对两个都进行排序,比较排完序之后的词
- Hash思想:
- 使用一个hash,基于字符串的特殊性,我们可以用[0, 255]或者[0…65535]的数组成,我们暂且认为他们都是小写英文字母,用[0…25],就是以下代码段中的num表示b中的单词出现了多少次【就是26个字母每个出现多少次】;
- 可以存一下有多少个非0次出现的,以后有用
int nonzero = 0 for i in range(0, len(b)): tmp = num[b[i] - 'a'] tmp += 1 if tmp== 1: nonzero += 1
- 我们用b中的次数减去a中一个“窗口”内的字符串种类,如果结果全是0,则找到这样的子串。注意:num[]的含义变为字符串的种类差
- 第一个窗口[0…len(b)-1]【lena < lenb无解】
- 窗口如何滑动?向右一定一位
- 新窗口a[i-lenb+1: i]
- 旧窗口a[i-lenb: i+1]
- 扔掉a[i-lenb]
- 加入a[i]
a = 'hello'
b = 'lel'
array = [0] * 26
no_zero = 0
for i in range(len(b)):
# 就是一个归一化操作,将c变成0-25期间
c = ord(a[i]) - ord('a')
array[c] -= 1
if array[c] == 0:
# 把一个非0值变成了0, 非0值减1
no_zero -= 1
elif array[c] == -1:
no_zero += 1
if no_zero == 0:
print(True)
else:
print(False)
for i in range(len(b), len(a)):
c = ord(a[i-len(b)]) - ord('a')
array[c] += 1
if array[c] == 1: no_zero += 1
elif array[c] == 0: no_zero -= 1
c = ord(a[i]) - ord('a')
array[c] -= 1
if array[c] == 0: no_zero -= 1
elif array[c] == 0: no_zero -= 1
if no_zero == 0:
print('True')
"""Leetcode 3"""
单词翻转
- 翻转句子中的全部单词,单词内容不变
- 例如:I’m a student,变为 student a I’m;
- in-place翻转,字符串第i位到第j位
- while i<j: swap(s[i++], s[j–])
- 有什么用?
- 翻转整个句子: tneduts a m’I
- 每个单词单独翻转:student a I’m
- 难点?如何区分单词?找空格,split
- 思考题:字符串循环移位abcd
- U移动一次变为bcda
- 移动2次,变为cdab
- 移动3次,变为dabc
- 结论:长度为n,移动m次,相当于移动了m%n次
- 前m%n位翻转,后n-m%n位翻转
- 总体在翻转一次,实验一下?
test = 'abcd'
x = test[: 2]
y = test[2: ]
print(x, y)
u = x[::-1]
v= y[::-1]
tmp = u+v
tmp1 = tmp[::-1]
print(tmp1)
总结
- 我理解的in-place
- 本身O(1)空间
- 递归,堆栈空间可以不考虑
- 原地相关的问题
- 字符串循环左移、右移动
- 快拍partition相关
- 滑动窗口
- 能够达到O(n)的时间复杂度
- O(1)的空间复杂度
- 规则相关:细致
- 匹配(暴力)–KMP比较少见
- Manacher–要求比较高的面试