最近大乖弟弟在准备考研,要复习数据结构,于是我也跟他一起研究起算法来了。今天遇到的问题是,获取字符串中,没有重复字符的最长一段字符串的长度。如下面字符串:
const str='Canyoufindthelongeststringinthissentence';
方法一:滑动窗口法,基本思路就是定义一个窗口在字符串上向右滑动,窗口右边滑动时,判断进入窗口的字符是否有重复。若有重复,则窗口左边也向右滑动,直到没有重复字符。在滑动过程中,记录窗口中没有重复字符的最大长度。
function findLongestStr(str) {
const strLen = str.length;
if(strLen<=1){
return strLen;
}else{
let strSet = new Set();
let left = 0, right = 0, maxLen = 0;
for(let i=0;i<strLen;i++){
const letter = str[i];
right++;
if(strSet.has(letter)){
while(left<=right && strSet.has(letter)){
strSet.delete(str[left]);
left++;
}
}
strSet.add(letter);
maxLen = Math.max(maxLen,right-left);
}
return maxLen;
}
};
方法一中包含一个for循环和一个while循环,在最坏的情况下,时间复杂度为O(n²)。我一直在考虑,是否可以只循环一遍,来实现这个功能呢?于是想到了方法二
方法二: 定义的变量比较多,属于滑动窗口法的变形。外层的循环是一样的,区别是使用Map替代Set,不仅存储了出现过的字符,还存储了字符最近一次出现的索引位置,这样就可以省去内层while循环,将时间复杂度降为O(n)。
function findLongestStr(str) {
const strLen = str.length;
if(strLen<=1){
return strLen;
}
let strMap = new Map();
let maxLen = 0, maxLexIdx = 0, curLen = 0, curLenIdx = 0;
for(let i=0; i<strLen; i++){
const letter = str[i];
const preIndex = strMap.get(letter);
if(preIndex!=undefined && preIndex>=curLenIdx){
if(curLen>maxLen){
maxLen = curLen;
maxLenIdx = curLenIdx;
}
curLenIdx = preIndex+1;
curLen = i - preIndex;
}else{
curLen++;
}
strMap.set(letter,i);
maxLen = Math.max(maxLen,curLen)
}
return maxLen;
};