题目说明
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
测试样例
输入:“abcabcbb”
输出:3
最长子串:“abc”
输入:“bbbbb”
输出:1
最长子串:“b”
输入:“pwwkew”
输出:3
最长子串:“wke”
题意理解
字符串处理题,实际上也是一个数组的问题,这里需要明确一个问题,这里所指的子串,是字符串的一个连续子序列,题目有以下几个要求
- 这个子串必须无重复元素
- 这个子串要求长度是最长的
解题方案
根据题目要求,我们很容易想到穷举所有子串判断其中最大值,这样很显然有着非常高的时间复杂度。
这里考虑到一个窗口的概念,这一概念在很多方面都有所应用,比如计算机网络方面使用窗口限制发送,接受数据序号等。
这里我们使用一个可变大小的滑动窗口表示当前不含重复元素子串的长度,有以下两个状态转移条件:
- 当最右元素加入窗口使得窗口扩大不破坏不重复原则时,将其加入并扩大窗口
- 当最右元素加入窗口破坏不重复原则,这时必然有一个元素与最右元素重复,那么只需要将窗口大小缩小到之前的重复元素之后即可
根据这两个转移条件,我们就可以完成合适的转移长度,寻找尽可能少的情况,时间复杂度为O(n)。
解决问题的关键在于如何确定重复元素的位置。
滑动窗口法
我在算法中使用map来存储元素最后出现的位置,如果出现重复时,前一元素的位置落在窗口之内,则会破坏不重复原则,那么需要进行更新。
算法如下:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int begin = 0,end = 0;
int maxlen = 0;
map<char,int> alph;//用于记录上次这个元素出现的位置
map<char,int>::iterator iter;
while(end<s.size()){
iter = alph.find(s[end]);
if(iter == alph.end()){//当前还未出现过该字符元素
alph.insert(pair<char,int>(s[end],end));//记录第一次出现的位置
end++;//下标移动到下一个位置
}else{
int lastind = iter->second;//上次出现的位置
iter->second = end;//更新元素最近所在位置
if(lastind>=begin){//如果出现在窗口之中,需要更新begin位置
maxlen = maxlen<end-begin?end-begin:maxlen;//确认当前窗口大小
begin = lastind+1;//移动begin
}
end++;
}
}
return maxlen>end-begin?maxlen:end-begin;
}
};
时间复杂度为O(n),运行结果如下:
所有代码都可以在我的github上找到:LeetCode