编程之法之字符串
字符串
1.1 字符串的旋转
(leetcode 796) 给定两个字符串, A 和 B。A 的旋转操作就是将 A 最左边的字符移动到最右边。 例如, 若 A = 'abcde',在移动一次之后结果就是'bcdea' 。如果在若干次旋转操作之后,A 能变成B,那么返回True。
注:主要需要考虑为空的情况,另外就是当num超过字符串长度
class Solution:
def rotateString(self, A: str, B: str) -> bool:
if not A and not B:
return True
if not A or not B:
return False
for i in range(len(A)+1):
if B == self.rotate(A,i):
return True
return False
def rotate(self,A,num):
indx = num%len(A)
return A[indx:] + A[:indx]
1.2 字符串的包含
给定两个分别由字母组成的字符串A和字符串B,字符串B的长度比字符串A短。请问,如何最快地判断字符串B中所有字母是否都在字符串A里?
def StringContain(strA,strB):
strHash = 0
for v in strA:
strHash |= (1<<(ord(v)-ord('A'))
for v in strB:
if (strHash & (1 << (ord(v)-ord('A')))==0):
return False
return True
1.3 字符串的全排列
(leetcode 46) 给定一个没有重复数字的序列,返回其所有可能的全排列。
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
self.visited = [0] * len(nums)
self.nums = nums
self.result = []
self.dfs([])
return self.result
def dfs(self,path):
if len(path) == len(self.nums):
self.result.append(path)
else:
for i in range(len(self.nums)):
if not self.visited[i]:
self.visited[i] = 1
self.dfs(path+[self.nums[i]])
self.visited[i] = 0
第二种方法比第一种要慢
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
self.result = []
self.perm(nums,0,len(nums))
return self.result
def perm(self,nums,fr,to):
if to <= 1:
self.result.append(nums)
return
if fr==to:
tmp = nums[:]
self.result.append(tmp)
else:
for i in range(fr,to):
nums[i],nums[fr] = nums[fr],nums[i]
self.perm(nums,fr+1,to)
nums[i],nums[fr] = nums[fr],nums[i]
1.3.1 字典序排列
(leetcode 31) 下一个排列。
实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
注:这道题就是典型的字典序排列问题,过程如下:
- 找到排列中最右边的一个升序首位位置i
- 如果i小于0,说明找不到,则直接返回;
- 找到排列中第i个位置右边最后一个比nums[i]大的位置j;
- 交换i和j位置的数;
- 把第i+1到最后的部分翻转;
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
# 找到最后一个升序首位
indx = -1
for i in range(len(nums)-2,-1,-1):
if nums[i] < nums[i+1]:
indx = i
break
# 已经找到所有的
if indx<0:
nums.sort()
return
# 找到右边最后一个比nums_i大的位置
k = len(nums)-1
for k in range(len(nums)-1,indx,-1):
if nums[k] > nums[indx]:
break
nums[indx], nums[k] = nums[k], nums[indx]
nums[indx+1:] = nums[indx+1:][::-1]
1.4 回文判断
(leetcode 234) 请判断一个链表是否为回文链表。
这道题比较经典,涉及到如何使用快慢表,如何翻转链表;
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
new_head = None
while slow:
p = slow
slow = slow.next
p.next = new_head
new_head = p
while new_head:
if new_head.val != head.val:
return False
new_head = new_head.next
head = head.next
return True
1.5 最长回文子串
(leetcode 5) 最长回文子串
注:注意对情况的讨论,第一种解法参考编程之法,原文中的代码在while语句中缺少边界判断,会报错。
class Solution:
def longestPalindrome(self, s: str) -> str:
if not s:
return s
s = "#".join(s.strip())
s = "#" + s + "#"
p = [0]*len(s)
mx = 0
for i in range(1,len(s)):
if mx > i:
p[i] = min(p[2*id-i],mx-i)
else:
p[i] = 1
while i+p[i] < len(s) and i-p[i] > 0 and (s[i+p[i]] == s[i-p[i]]):
p[i] += 1
if p[i] + i > mx:
mx = p[i] + i
id = i
l = max(p)-1
indx = p.index(l+1)
maxs = s[indx-l:indx+l+1]
return "".join(maxs.split("#"))
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
maxl = 0
start = 0
for i in range(n):
if i-maxl >= 1 and s[i-maxl-1:i+1] == s[i-maxl-1:i+1][::-1]:
start = i - maxl - 1
maxl += 2
continue
if i-maxl >= 0 and s[i-maxl:i+1] == s[i-maxl:i+1][::-1]:
start = i - maxl
maxl += 1
return s[start:start+maxl]