原理:
左右の 2 つのポインターを定義して、2 つのポインターに対応する部分文字列に繰り返し文字がないことを確認し、最長の部分文字列の長さを見つけて記録します。ウィンドウが条件を満たしている場合は、右ポインタを右にスライドしてウィンドウを拡大し、最適値を更新します。ウィンドウが条件を満たしていない場合は、左ポインタを右にスライドしてウィンドウを縮小します。(ここでは、データ構造 - ハッシュセットを使用して、繰り返し文字があるかどうかを判断する必要があります)
LeetCode 公式ウェブサイト - 世界中のオタクに愛されるテクノロジー成長プラットフォーム
トピック:
n
要素のソートされた (昇順) 整数配列 nums
とターゲット値を 指定して 、ターゲット値が存在する場合はインデックス を返し 、そうでない場合は を返すtarget
関数を作成します 。nums
target
-1
例:
入力: nums= [-1,0,3,5,9,12],target=9 出力: 4
ヒント:
- のすべての要素が一意であると想定できます
nums
。n
その間になります[1, 10000]
。
nums
の各要素は間にあります[-9999, 9999]
。
アイデア 1: 引き違い窓
左ポインタを 1 スペース右に移動すると、開始位置として次の文字の列挙が開始されます。その後、右ポインタを右に移動し続けることができますが、対応する部分文字列に繰り返し文字がないことを確認する必要があります。これら 2 つのポインタへ。移動が完了すると、この部分文字列は、左ポインタから始まり、繰り返し文字を含まない最長の部分文字列に対応し、この部分文字列の長さを記録します。列挙の最後に、見つかった最長の部分文字列の長さが答えになります。
上記のプロセスでは、繰り返し文字があるかどうかを判断するためにデータ構造も使用する必要があります。一般的に使用されるデータ構造はハッシュ セットです(つまり、C++ の std::unowned_set、Java の HashSet、Python の set、JavaScript の Set))。
左ポインタが右に移動すると、ハッシュ セットから文字が削除され、右ポインタが右に移動すると、ハッシュ セットに文字が追加されます。
答え:
①方法1:暴力的な解決策
部分文字列を 1 つずつ生成して、繰り返しの文字が含まれていないかどうかを確認します。
このメソッドは実装できますが、十分にエレガントではありません。【208ミリ秒 47.31MB】
var lengthOfLongestSubstring = function (s) {
// 左右指针
let left = (right = max = 0);
let val = (endVal = "");
let length = s.length;
while (right < length) {
endVal = s.charAt(right);
if (val.indexOf(endVal) < 0) {
val = val + endVal;
// 右指针向右移动
right++;
if (right >= length) {
max = max > val.length ? max : val.length;
}
} else {
max = max > val.length ? max : val.length;
left++;
right = left + 1;
val = s.charAt(left);
}
}
return max;
};
console.log("答案", lengthOfLongestSubstring("abcabcbb")); // 3
console.log("答案", lengthOfLongestSubstring("")); // 0
console.log("答案", lengthOfLongestSubstring(" ")); // 1
console.log("答案", lengthOfLongestSubstring(" ")); // 1
console.log("答案", lengthOfLongestSubstring("aa")); // 1
console.log("答案", lengthOfLongestSubstring("ab")); // 2
console.log("答案", lengthOfLongestSubstring("pwwkew")); // 3
② 方法 2: スライディング ウィンドウと最適化
キーワード: 繰り返し文字 --> 1 回出現
パターン認識 1: 出現回数が関係したら、ハッシュ テーブルが必要になる
部分文字列を構築し、添え字をハッシュ テーブルに保存する;
パターン認識 2: 部分文字列を含む、スライディング ウィンドウ を考慮する
回答 1: 公式ソリューション [96 ミリ秒 45.49 MB]
var lengthOfLongestSubstring = function (s) {
// 哈希集合,记录每个字符是否出现过
const occ = new Set();
const length = s.length;
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
let start = -1,
res = 0;
for (let i = 0; i < length; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.delete(s.charAt(i - 1));
}
while (start + 1 < length && !occ.has(s.charAt(start + 1))) {
// 不断地移动右指针
occ.add(s.charAt(start + 1));
++start;
}
// 第 i 到 start 个字符是一个极长的无重复字符子串
res = Math.max(res, start - i + 1);
}
return res;
};
console.log("答案", lengthOfLongestSubstring("abcabcbb")); // 3
console.log("答案", lengthOfLongestSubstring("")); // 0
console.log("答案", lengthOfLongestSubstring(" ")); // 1
console.log("答案", lengthOfLongestSubstring(" ")); // 1
console.log("答案", lengthOfLongestSubstring("aa")); // 1
console.log("答案", lengthOfLongestSubstring("ab")); // 2
console.log("答案", lengthOfLongestSubstring("pwwkew")); // 3
回答 2: 個人用 [88 ミリ秒 45.73 MB]
var lengthOfLongestSubstring = function (s) {
// 定义左指针、右指针、长度、最大长度
let left = (right = length = maxLength = 0)
// 定义set集合
let set = new Set()
// 遍历字符串
while (right < s.length) {
if (!set.has(s[right])) {
// 不存在相同字符,把当前字符添加到字符集中
set.add(s[right])
// 更新长度
length++
if (length > maxLength) {
maxLength = length
}
// 右指针往右移动
right++
} else {
// 删除字符集中的第一个字符
set.delete(s[left])
// 更新长度
length--
// 左指针往右移动
left++
}
}
return maxLength
};
回答 3: ビデオ [68 ミリ秒 45.91 MB]
var lengthOfLongestSubstring = function (s) {
// 定义左指针、右指针、长度、最大长度
let left = (right = length = maxLength = 0)
// 定义set集合
let set = new Set()
// 遍历字符串
while (right < s.length) {
if (!set.has(s[right])) {
// 不存在相同字符,把当前字符添加到字符集中
set.add(s[right])
// 更新长度
length++
if (length > maxLength) {
maxLength = length
}
// 右指针往右移动
right++
} else {
while (set.has(s[right])) {
// 删除字符集中的第一个字符
set.delete(s[left])
// 更新长度
length--
// 左指针往右移动
left++
}
set.add(s[right])
length++
right++
}
}
return maxLength
};
ビデオ:
スライディング ウィンドウのアイデアを理解するためのアルゴリズムの質問! 【楽しいリートコード】No.3 重複文字を含まない最長の部分文字列_bilibili_bilibili