滑动窗口
1、题目:无重复字符的最长子串
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
题解思路:
本题可以采用滑动窗口的思路,start指向窗口的的起点(当前窗口的第一个字符),end指向窗口的终点(当前窗口的最后一个字符),开始时,start和end指向字符串的第一个字符,开始移动end,每次都从start开始比较直到end-1,看是否有跟end指向的字符相同,如果相同,那么start+1,end+1;否则只是end+1,最后end>s.length,循环结束。
完整代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int start = 0;
int end = 0;
int length = 0;//当前字符串长度
int maxLen = 0;//最大字符串长度
while(end < s.size()) {
char tmpchar = s[end];//指向滑动窗口的最后一个字符串
for(int index = start; index < end; index++) {
if(tmpchar == s[index]) {//从start到end之间只要有一个相等的字符串,那么滑动窗口大小减1
start = index + 1;
length = end - start;
break;
}
}
length++;
end++;
maxLen = max(maxLen, length);
}
return maxLen;
}
};
使用Python3的简单写法:将字符串一个个依此入队中,如果有相同的字符串就将队首的那个相同的字符串出队,注意:不管是否有元素出队,都要将字符将下一个元素入队即window.append(s[indedx])操作
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
maxLen, window = 0, []
for index in range(len(s)):
while(s[index] in window):
window.pop(0)#有重复就左端输出重复那个元素
window.append(s[index])#不管有没有重复的的元素都要将新的元素推入窗口中
maxLen = max(maxLen, len(window))
return maxLen
类似的题目1:209. 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
算法思路:将数组nums里的元素依次一个个入队window,要是sum(window)超过target值就减掉一个,要是没超过就继续加,直至满足条件。
完整代码:(思路大同小异也是用滑动窗口)
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
minLen, window, sum = float('inf'), [], 0 # minLen表示最短数组长度,window表示存放当前元素的容器
for index in range(len(nums)):
sum += nums[index]
window.append(nums[index]) # 每加一个元素都要入队一个元素
while(sum >= target):# 如果sum值超过目标值,则我们需要出队一些元素
sum -= window[0] # 先减去队列的第一个元素
minLen = min(minLen, len(window))
window.pop(0) # 出队一个元素
if(minLen == float('inf')): #target值不在列表里
return 0
else:
return minLen
另外一种写法:(类似于双指针的方式,left和right维护一个滑动窗口)
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
left = right = 0 #滑动窗口的左右两个端点
minLen = float('inf') #初始化长度最小的 连续子数组的长度
sum = 0 #记录当前窗口里元素的之和
length = len(nums) #记录列表里元素的个数
while(right < length):
sum += nums[right]
while(sum >= target):
sum -= nums[left] # 去掉左侧的数
minLen = min(minLen, right - left + 1)
left += 1 # 左边要剔除的数可能不止一个
right += 1
if(minLen == float('inf')): #target值不在列表里
return 0
else:
return minLen
总结:滑动窗口模板
《挑战程序设计竞赛》这本书中把滑动窗口叫做「虫取法」,我觉得非常生动形象。因为滑动窗口的两个指针移动的过程和虫子爬动的过程非常像:前脚不动,把后脚移动过来;后脚不动,把前脚向前移动。
我分享一个滑动窗口的模板,能解决大多数的滑动窗口问题:
def findSubArray(nums):
N = len(nums) # 数组/字符串长度
left, right = 0, 0 # 双指针,表示当前遍历的区间[left, right],闭区间
sums = 0 # 用于统计 子数组/子区间 是否有效,根据题目可能会改成求和/计数
res = 0 # 保存最大的满足题目要求的 子数组/子串 长度
while right < N: # 当右边的指针没有搜索到 数组/字符串 的结尾
sums += nums[right] # 增加当前右边指针的数字/字符的求和/计数
while 区间[left, right]不符合题意: # 此时需要一直移动左指针,直至找到一个符合题意的区间
sums -= nums[left] # 移动左指针前需要从counter中减少left位置字符的求和/计数
left += 1 # 真正的移动左指针,注意不能跟上面一行代码写反
# 到 while 结束时,我们找到了一个符合题意要求的 子数组/子串
res = max(res, right - left + 1) # 需要更新结果
right += 1 # 移动右指针,去探索新的区间
return res
滑动窗口中用到了左右两个指针,它们移动的思路是:以右指针作为驱动,拖着左指针向前走。右指针每次只移动一步,而左指针在内部 while 循环中每次可能移动多步。右指针是主动前移,探索未知的新区域;左指针是被迫移动,负责寻找满足题意的区间。
以下参考类似题1004:
类似的题目2: 643.子数组最大平均数I
给你一个由 n
个元素组成的整数数组 nums
和一个整数 k
。
请你找出平均数最大且 长度为 k
的连续子数组,并输出该最大平均数。
任何误差小于 10-5
的答案都将被视为正确答案。
示例 1:
输入:nums = [1,12,-5,-6,50,3], k = 4
输出:12.75
解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
示例 2:
输入:nums = [5], k = 1
输出:5.00000
提示:
n == nums.length
1 <= k <= n <= 105
-104 <= nums[i] <= 104
完整代码:
class Solution:
def findMaxAverage(self, nums: List[int], k: int) -> float:
sum, maxAvg = 0, -math.inf
left = right =0; #维护一个滑动窗口
count = 0 #统计窗口元素个数是否为k
for right in range(k): #先把k个值的均值存起来
sum += nums[right]
if(maxAvg < sum/k):
maxAvg = sum/k
for left in range(len(nums)):
right += 1#右滑一格
if(right < len(nums)):
sum -= nums[left]#剔除滑动窗口的最左边的元素
sum += nums[right]#在窗口的右边加入一个新的元素
maxAvg = max(maxAvg, sum/k)
return maxAvg
类似题目3: 1695. 删除子数组的最大得分
给你一个正整数数组 nums
,请你从中删除一个含有 若干不同元素 的子数组。删除子数组的 得分 就是子数组各元素之 和 。
返回 只删除一个 子数组可获得的 最大得分 。
如果数组 b
是数组 a
的一个连续子序列,即如果它等于 a[l],a[l+1],...,a[r]
,那么它就是 a
的一个子数组。
示例 1:
输入:nums = [4,2,4,5,6]
输出:17
解释:最优子数组是 [2,4,5,6]
示例 2:
输入:nums = [5,2,1,2,5,2,1,2,5]
输出:8
解释:最优子数组是 [5,2,1] 或 [1,2,5]
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 104
题解一:(方法可行但是超时)
class Solution:
def maximumUniqueSubarray(self, nums: List[int]) -> int:
sum, window, maxArray = 0, [], -math.inf
for index in range(len(nums)):
while(nums[index] in window):
sum -= window[0]#需要减掉重复的元素
window.pop(0) #把重复的元素弹出
sum += nums[index]
window.append(nums[index])
maxArray = max(maxArray, sum)
return maxArray
题解二:本质上还是采用left和right两个指针维护一个滑动窗口,left指向窗口的头,right指向窗口的尾,每次添加新的元素都是在窗口的尾部添加,当添加一个元素值value在窗口里已经存在时,那么我们需要将该重复的元素值value之前的所有元素全部清理出窗口(包含这个重复的元素值value),移除结束后,left指向的是原来窗口里value值的下一个值的位置,在移除过程中,sum也要注意减去相应的移除的值。当添加新的元素不在窗口里时,直接添加到尾部即可,right向右滑动一格。
class Solution:
def maximumUniqueSubarray(self, nums: List[int]) -> int:
sum, window, maxArray = 0, set(), -math.inf
left = right = 0 #维护一个滑动窗口
while(right < len(nums)):
while(nums[right] in window):
sum -= nums[left] #需要从窗口的最左边开始减掉重复元素之前的所有元素
window.remove(nums[left]) #把最左边重复的元素弹出
left += 1#元素少一个,滑动窗口右移一格
sum += nums[right]
window.add(nums[right])
right += 1 #元素加一个,滑动窗口右移一格
maxArray = max(maxArray, sum)
return maxArray
类似题目4: 438. 找到字符串中所有字母异位词
给定两个字符串 s
和 p
,找到 s
中所有 p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
提示:
1 <= s.length, p.length <= 3 * 104
s
和p
仅包含小写字母
算法思路:首先先对待比较的字符串p进行排序,然后在串s中开辟出和p等长的窗口大小的子串,对该字串进行排序,若和p相等则为异位词,然后窗口向右滑动一格,直至到s末端。
完整代码:
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
res = []#最终的结果数组
p = sorted(p) #先对p的字符排序
for index in range(len(s)- (len(p)-1)):
if(sorted(s[index:index + len(p)]) == p):#对字符串s的子串在大小为len(p)的窗口内对字符串进行排序
#若和p相等则为异位词
res.append(index)
return res
类似题目5: 567. 字符串的排列
给你两个字符串 s1
和 s2
,写一个函数来判断 s2
是否包含 s1
的排列。如果是,返回 true
;否则,返回 false
。
换句话说,s1
的排列之一是 s2
的 子串 。
示例 1:
输入:s1 = "ab" s2 = "eidbaooo"
输出:true
解释:s2 包含 s1 的排列之一 ("ba").
示例 2:
输入:s1= "ab" s2 = "eidboaoo"
输出:false
提示:
1 <= s1.length, s2.length <= 104
s1
和s2
仅包含小写字母
算法思路:和 类似题目4: 438. 找到字符串中所有字母异位词思路一模一样
完整代码:
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
res = False
s1 = sorted(s1)
for index in range(len(s2) - (len(s1) - 1)):
if(sorted(s2[index:index+len(s1)]) == s1):
res = True
break
return res
类似题目6:1004. 最大连续1的个数 III
给定一个二进制数组 nums
和一个整数 k
,如果可以翻转最多 k
个 0
,则返回 数组中连续 1
的最大个数 。
示例 1:
输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2
输出:6
解释:[1,1,1,0,0,1,1,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 6。
示例 2:
输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3
输出:10
解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1]
粗体数字从 0 翻转到 1,最长的子数组长度为 10。
提示:
1 <= nums.length <= 105
nums[i]
不是0
就是1
0 <= k <= nums.length
实例:
以 A= [1,1,1,0,0,0,1,1,1,1,0], K = 2
为例,下面的动图演示了滑动窗口的两个指针的移动情况。
实现代码:
class Solution:
def longestOnes(self, nums: List[int], k: int) -> int:
left = right = 0 #维护一格滑动窗口
count = 0 #统计nums里0的个数
res = 0 #统计最大连续1的个数
while(right < len(nums)):
if(nums[right] == 0):
count += 1
while(count > k):#当窗口里的0的个数超过给的k个,此时需要窗口头部右移去掉0
if(nums[left] == 0):
count -= 1
left += 1
res = max(res, right - left + 1)
right += 1
return res
类似题目7:1208. 尽可能使字符串相等
给你两个长度相同的字符串,s
和 t
。
将 s
中的第 i
个字符变到 t
中的第 i
个字符需要 |s[i] - t[i]|
的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。
用于变更字符串的最大预算是 maxCost
。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。
如果你可以将 s
的子字符串转化为它在 t
中对应的子字符串,则返回可以转化的最大长度。
如果 s
中没有子字符串可以转化成 t
中对应的子字符串,则返回 0
。
示例 1:
输入:s = "abcd", t = "bcdf", maxCost = 3
输出:3
解释:s 中的 "abc" 可以变为 "bcd"。开销为 3,所以最大长度为 3。
示例 2:
输入:s = "abcd", t = "cdef", maxCost = 3
输出:1
解释:s 中的任一字符要想变成 t 中对应的字符,其开销都是 2。因此,最大长度为1
示例 3:
输入:s = "abcd", t = "acde", maxCost = 0
输出:1
解释:a -> a, cost = 0,字符串未发生变化,所以最大长度为 1。
提示:
1 <= s.length, t.length <= 10^5
0 <= maxCost <= 10^6
s
和t
都只含小写英文字母。
实现代码:
class Solution:
def equalSubstring(self, s: str, t: str, maxCost: int) -> int:
left = right = 0 #维护一格滑动窗口
totalCost = 0 #转化的总的开销
maxLen = -math.inf #记录可以转化的最大长度
while(right < len(s)):
#在python之中ord是用来返回一个字符的ASCII码数值的
totalCost += abs(ord(s[right]) - ord(t[right]))
while(totalCost > maxCost): #不符合题意:
# 此时需要一直移动左指针,直至找到一个符合题意的区间
totalCost -= abs(ord(s[left]) - ord(t[left]))
left += 1
maxLen = max(maxLen, right - left + 1)
right += 1
return maxLen
类似题目8:1052. 爱生气的书店老板
有一个书店老板,他的书店开了 n
分钟。每分钟都有一些顾客进入这家商店。给定一个长度为 n
的整数数组 customers
,其中 customers[i]
是在第 i
分钟开始时进入商店的顾客数量,所有这些顾客在第 i
分钟结束后离开。
在某些时候,书店老板会生气。 如果书店老板在第 i
分钟生气,那么 grumpy[i] = 1
,否则 grumpy[i] = 0
。
当书店老板生气时,那一分钟的顾客就会不满意,若老板不生气则顾客是满意的。
书店老板知道一个秘密技巧,能抑制自己的情绪,可以让自己连续 minutes
分钟不生气,但却只能使用一次。
请你返回 这一天营业下来,最多有多少客户能够感到满意 。
示例 1:
输入:customers = [1,0,1,2,1,1,7,5], grumpy = [0,1,0,1,0,1,0,1], minutes = 3
输出:16
解释:书店老板在最后 3 分钟保持冷静。
感到满意的最大客户数量 = 1 + 1 + 1 + 1 + 7 + 5 = 16.
示例 2:
输入:customers = [1], grumpy = [0], minutes = 1
输出:1
提示:
n == customers.length == grumpy.length
1 <= minutes <= n <= 2 * 104
0 <= customers[i] <= 1000
grumpy[i] == 0 or 1
算法思路:
由于「技巧」只会将情绪将「生气」变为「不生气」,不生气仍然是不生气。
- 我们可以先将原本就满意的客户加入答案,同时将对应的 customers[i]变为 0。
- 之后的问题转化为:在 customers 中找到连续一段长度为 minutes的子数组,使得其总和最大。这部分就是我们应用技巧所得到的客户。
实现代码:
class Solution:
def maxSatisfied(self, customers: List[int], grumpy: List[int], minutes: int) -> int:
sum = 0
#先遍历一遍customers目的是把那些原本满意的客户数量求出来
for i in range(len(customers)):
if(grumpy[i] == 0):
sum += customers[i]
customers[i] = 0 #目的是为了二次遍历customers避免重复相加
cur, res = 0, 0 #cur记录当前的最大客户数量,res记录最终的最大客户数量
left,right = 0, 0 #维护一个滑动窗口
for right in range(len(customers)):
cur += customers[right]
if(right >= minutes):
cur -= customers[left]#移除窗口头部第一个元素
left += 1#窗口右移一格
res = max(res, cur)
return sum + res
类似题目9:1423. 可获得的最大点数
几张卡牌 排成一行,每张卡牌都有一个对应的点数。点数由整数数组 cardPoints
给出。
每次行动,你可以从行的开头或者末尾拿一张卡牌,最终你必须正好拿 k
张卡牌。
你的点数就是你拿到手中的所有卡牌的点数之和。
给你一个整数数组 cardPoints
和整数 k
,请你返回可以获得的最大点数。
示例 1:
输入:cardPoints = [1,2,3,4,5,6,1], k = 3
输出:12
解释:第一次行动,不管拿哪张牌,你的点数总是 1 。但是,先拿最右边的卡牌将会最大化你的可获得点数。最优策略是拿右边的三张牌,最终点数为 1 + 6 + 5 = 12 。
示例 2:
输入:cardPoints = [2,2,2], k = 2
输出:4
解释:无论你拿起哪两张卡牌,可获得的点数总是 4 。
示例 3:
输入:cardPoints = [9,7,7,9,7,7,9], k = 7
输出:55
解释:你必须拿起所有卡牌,可以获得的点数为所有卡牌的点数之和。
示例 4:
输入:cardPoints = [1,1000,1], k = 1
输出:1
解释:你无法拿到中间那张卡牌,所以可以获得的最大点数为 1 。
示例 5:
输入:cardPoints = [1,79,80,1,1,1,200,1], k = 3
输出:202
提示:
1 <= cardPoints.length <= 10^5
1 <= cardPoints[i] <= 10^4
1 <= k <= cardPoints.length
算法思路:维护一个len-k的窗口,保证窗口里面和最小,然后剩余的k个数的和就是最大 。首先放len(cardPoints)-k个数进窗口里,然后在区间(len(cardPoints) - k), len(cardPoints)里滑动窗口,逐步寻找到连续的len(cardPoints)-k个数和的最小值。
实现代码:
class Solution:
def maxScore(self, cardPoints: List[int], k: int) -> int:
#题目可以转化为维护一个len-k的窗口,保证窗口里面和最小,然后剩余的k个数的和就是最大
sum = 0
for i in range(len(cardPoints)):
sum += cardPoints[i]
left = right = 0 #维护一个大小为len(cardPoints)-k的窗口
minValue, cur = 0, 0
#假设先从最右边取k个值即先放len(cardPoints)-k值进窗口里
for i in range(len(cardPoints) - k):
minValue += cardPoints[i]
cur = minValue
#在区间(len(cardPoints) - k), len(cardPoints)里滑动窗口
for right in range((len(cardPoints) - k), len(cardPoints)):
cur += cardPoints[right]
if(right >= (len(cardPoints)-k)):#当窗口里元素个数超过len(cardPoints)-k
cur -= cardPoints[left]
left += 1#开始右移窗口
minValue = min(minValue, cur)
return sum - minValue
类似题目10:1838. 最高频元素的频数 前缀➕滑动窗口
元素的 频数 是该元素在一个数组中出现的次数。
给你一个整数数组 nums
和一个整数 k
。在一步操作中,你可以选择 nums
的一个下标,并将该下标对应元素的值增加 1
。
执行最多 k
次操作后,返回数组中最高频元素的 最大可能频数 。
示例 1:
输入:nums = [1,2,4], k = 5
输出:3
解释:对第一个元素执行 3 次递增操作,对第二个元素执 2 次递增操作,此时 nums = [4,4,4] 。
4 是数组中最高频元素,频数是 3 。
示例 2:
输入:nums = [1,4,8,13], k = 5
输出:2
解释:存在多种最优解决方案:
- 对第一个元素执行 3 次递增操作,此时 nums = [4,4,8,13] 。4 是数组中最高频元素,频数是 2 。
- 对第二个元素执行 4 次递增操作,此时 nums = [1,8,8,13] 。8 是数组中最高频元素,频数是 2 。
- 对第三个元素执行 5 次递增操作,此时 nums = [1,4,13,13] 。13 是数组中最高频元素,频数是 2 。
示例 3:
输入:nums = [3,9,6], k = 2
输出:1
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 105
1 <= k <= 105
算法思路:
首先很容易想到,先对数组进行排序。
接下来,我们只需要找到某一段区间内,每个值与该区间内最后一个值相差的总和,不超过目标k的最大值。
举个例子:
数组:1,2,4,6 k:5
第一次在【1,2】范围内,如果要让1变成2,那么就加1即可。
第二次扩大到【1,2,4】范围内,如果要让1变成4,2变成4,那么一共需要增加5次。
第三次扩大到【1,2,4,6】范围内,依次类推,每个值变为6,一共需要11次。(此时11次已经超过了5次的操作上限,那么就滑动窗口的区间)
接下来,我们只需要依次判断窗口内满足条件的最大值即可。
简单的图例:
第一次区间内,需要加1(满足),实际上窗口里是2 2
第二次区间内,需要加5(满足)实际上窗口里是4 4 4,所以当前情况下的区间和 sum += (4-2)*(3-1) , 2表示前一次窗口里的数(2 2),4表示窗口新加入的数,3-1表示窗口里表示在4之前有多少个数
第三次区间内,需要加11(不满足) 实际上窗口里是6 6 6 6
由于超过了目标值5,所以窗口左侧,向右移动一位。实际上窗口里是 6 6 6
接下来,依此类推。
实现代码:
lass Solution:
def maxFrequency(self, nums: List[int], k: int) -> int:
nums = sorted(nums)
res = 1 #表示最终的最高频元素的频数
left, right = 0, 1 #left和right维护一个滑动窗口
#注:right指向的是那个可能成为最高频元素的频数的数
# 窗口里的数都是操作过后的数即全是一样的数
sum = 0 #表示操作的总次数
while(right < len(nums)):
sum += (nums[right]- nums[right-1]) * (right - left)
while(sum > k):
sum -= (nums[right] - nums[left])#需要移除窗口左端移除并减去之前相应加上的操作数
left += 1
res = max(res, right - left + 1)
right += 1
return res
字符串前缀
2、题目:最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成
(1)解题思路1:
首先我们需要有一个求解两个字符串的最长公共前缀的函数,接着就是遍历这个数组字符串,依次比较字符串,求解整个数组字符串的最长公共前缀。
完整代码:
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.size() == 0){
return "";
}
string prefix = strs[0];
for(int i = 1; i < strs.size(); i++){
prefix = longestCommonPrefix(prefix, strs[i]);
if(prefix.size() == 0){
return "";
break;
}
}
return prefix;
}
string longestCommonPrefix(string& s1, string& s2){
int len = min(s1.size(), s2.size());
int index = 0;//统计字串的长度
while(index <= len && s1[index] == s2[index]){
index++;
}
return s1.substr(0, index);//返回俩个字符串的公共子串
}
};
注意一个函数:return s1.substr(0, index) 返回是一个字符串的子串。
(2)解题思路2:
可以先对这个数组字符串进行排序,排序得到的第一个字符串为当前数组里最小的字符串,最后一个为最大的字符串,依次比较这两个字符串里的字符即可求出整个数组字符串的最长公共前缀。
注意一个函数:return start.substr(0, index) 返回是一个字符串的子串。
完整代码:
class Solution {
public:
string longestCommonPrefix(vector<string>& strs){
if(strs.size() == 0) return "";
sort(strs.begin(), strs.end());
string start = strs.front(), end = strs.back();//取出最短和最长的两个字符串进行比较
int len = min(start.size(), end.size());
int index = 0;
while(index < len && start[index] == end[index]){
index++;
}
return start.substr(0, index);
}
};
Python的简便代码:
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
if not strs:
return ""
# minStr = min(strs, key = len)#求列表str里最短的那个字符串
# maxStr = max(strs, key = len)#求列表str里最长的那个字符串
strs = sorted(strs)
start, end = strs[0], strs[len(strs)-1]
res = ""
for i in range(min(len(start), len(end))):
if(start[i] == end[i]):
res += start[i]
else:
break
return res
类似题目1:1392. 最长快乐前缀
「快乐前缀」 是在原字符串中既是 非空 前缀也是后缀(不包括原字符串自身)的字符串。
给你一个字符串 s
,请你返回它的 最长快乐前缀。如果不存在满足题意的前缀,则返回一个空字符串 ""
。
示例 1:
输入:s = "level"
输出:"l"
解释:不包括 s 自己,一共有 4 个前缀("l", "le", "lev", "leve")和 4 个后缀("l", "el", "vel", "evel")。最长的既是前缀也是后缀的字符串是 "l" 。
示例 2:
输入:s = "ababab"
输出:"abab"
解释:"abab" 是最长的既是前缀也是后缀的字符串。题目允许前后缀在原字符串中重叠。
提示:
1 <= s.length <= 105
s
只含有小写英文字母
算法思路:
直接切片,finalRes做循环内局部变量,遇到更大快乐前缀就覆盖.
实现代码:
class Solution:
def longestPrefix(self, s: str) -> str:
# beforePrefix, afterPrefix = [], []
# res1, res2 = '', ''
# for i in range(len(s)-1):
# res1 += s[i]
# beforePrefix.append(res1)
# temp = s[::-1]
# for i in range(len(s)-1):
# res2 += temp[i]
# res2 = res2[::-1]
# afterPrefix.append(res2)
# res2 = res2[::-1]
# finalRes = []
# print(beforePrefix)
# print(afterPrefix)
# # for i in range(len(beforePrefix)):
# # for j in range(len(afterPrefix)):
# # if(beforePrefix[i] == afterPrefix[j]):
# # finalRes.append(beforePrefix[i])
# finalRes = set(beforePrefix).intersection(afterPrefix)#求两个集合的交集
# max_length, longest_string = 0, ''
# for i in finalRes:
# if len(i) > max_length:
# max_length = len(i)
# longest_string = i
# return longest_string
res = ''
for i in range(len(s)):
if(s[:i] == s[len(s)-i:]):#遇到更大快乐前缀就覆盖
res = s[:i]
return res
类似题目2:14. 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""
。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀.
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i]
仅由小写英文字母组成
实现代码:
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
if not strs:
return ""
# minStr = min(strs, key = len)#求列表str里最短的那个字符串
# maxStr = max(strs, key = len)#求列表str里最长的那个字符串
strs = sorted(strs)
start, end = strs[0], strs[len(strs)-1]
#start是可能出现公共前缀最长的字符串,end是可能出现公共前缀最短的字符串
res = ""
for i in range(min(len(start), len(end))):#类似短板效应
if(start[i] == end[i]):
res += start[i]
else:
break
return res
类似题目3:1961. 检查字符串是否为数组前缀
给你一个字符串 s
和一个字符串数组 words
,请你判断 s
是否为 words
的 前缀字符串 。
字符串 s
要成为 words
的 前缀字符串 ,需要满足:s
可以由 words
中的前 k
(k
为 正数 )个字符串按顺序相连得到,且 k
不超过 words.length
。
如果 s
是 words
的 前缀字符串 ,返回 true
;否则,返回 false
。
示例 1:
输入:s = "iloveleetcode", words = ["i","love","leetcode","apples"]
输出:true
解释:
s 可以由 "i"、"love" 和 "leetcode" 相连得到。
示例 2:
输入:s = "iloveleetcode", words = ["apples","i","love","leetcode"]
输出:false
解释:
数组的前缀相连无法得到 s 。
提示:
1 <= words.length <= 100
1 <= words[i].length <= 20
1 <= s.length <= 1000
words[i]
和s
仅由小写英文字母组成
实现代码:
class Solution:
def isPrefixString(self, s: str, words: List[str]) -> bool:
res = ''
for i in range(len(words)):
if(words[i] in s and len(res) < len(s)):
res += words[i]
else:
break
return (res == s)
3、题目:回文数
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
例如,121 是回文,而 123 不是。
示例 1:
输入:x = 121
输出:true
示例 2:
输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。
提示:
-231 <= x <= 231 - 1
解题思路:
将数字反转再与原数字比较即可,一样就为回文数,不一样就不是。
数字反转代码:
while (x) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}
实现代码:
class Solution {
public:
bool isPalindrome(int x) {
long temp, res = 0;
bool flag = false;
if(x < 0 || (x % 10 == 0 && x != 0)){
flag = false;
}else{
int num = x;//先取出x,为了方便下面res和x值的比较
while(num){
temp = num % 10;
res = res * 10 + temp;
num /= 10;
}
if(res == x){
flag = true;
}
}
return flag;
}
};