给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
题目分析
此题与《剑指offer》48题类似,可以使用动态规划的思想解决。
-
动态规划
状态表示:设f(i)为第i个字符为结尾的不包含重复的子字符串的最大长度
状态转移:当第i个字符没有在i-1个字符中出现时,f(i) = f(i-1)+1;
当第i个字符在i-1个字符中出现过,首先计算第i个字符和上次出现位置的距离d:如果d小于f(i-1),即之前的最长子字符串中包含该字符,此时截取距离为d的子字符串为新的无重复字符子字符串;如果d大于f(i-1),即之前的最长子字符串不包含该字符,则加1即可
确定边界:到f(n)结束
状态转移方程:假设当前的最大长度为maxlength,则有
maxlength(i) = max(f(i-1), f(i-1)+1)
代码实现
实现时与《剑指offer》不同,这里的字符不限于a-z 26个字符。
class Solution: def lengthOfLongestSubstring(self, string): curLength = 0 maxLength = 0 position = {} for i in range(len(string)): if string[i] in position.keys(): preindex = position[string[i]] else: preindex = -1 if preindex <0 or i-preindex > curLength: #没有出现过或者距离大于以i-1为结尾的最大子字符串长度,则f(i) = f(i-1) +1 curLength +=1 else: if curLength >maxLength: maxLength = curLength #设置新的子字符串的长度 curLength = i - preindex position[string[i]] = i if curLength > maxLength: maxLength = curLength return maxLength print(Solution().GetMaxSubStr('arabcacfr'))
解法2:滑动窗口
-
使用两个指针表示字符串中的某个子串(左右边界)。
-
从左向右遍历,左指针逐步向右移动一格,表示开始枚举下一个字符作为起始位置,然后不断向右移动有指针,直到遇到以左指针相同的字符。此时得到的是以左指针字符开始的不包含重复字符的最长子串。
-
判断重复字符
-
C++
std::unordered_set
-
Java
HashSet
-
Python
set
-
JavaScript
set
-
代码实现
class Solution: def lengthOfLongestSubstring(self, s:str)->int: positions = set() n = len(s) pright, maxlength = 0 for i in range(n): if !=0: position.remove(s[i-1]) #从新的字符开始计算,去掉之前的 # 当right指向的字符在之前的position记录中没有出现,则添加 while pright + 1 < n and s[pright+1] not in positions: position.add(s[pright+1]) pright +=1 # 比较当前字符串与之前统计的最长字符串 maxlength = max(maxlength, pright - i + 1) return maxlength