Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions 3_LongestSubstringWithoutRepeatingCharacters/brute_force.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int longest_length = 0;

for (int start = 0; start < s.size(); start++) {
set<char> unique_letters;
for (int end = start; end < s.size(); end++) {
if (unique_letters.contains(s[end])) {
break;
}
unique_letters.insert(s[end]);
longest_length = max(longest_length, end - start + 1);
}
}

return longest_length;
}
};
44 changes: 44 additions & 0 deletions 3_LongestSubstringWithoutRepeatingCharacters/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## ステップ1
まず思いついた方法は総当たりで解く方法。
substringを作るための開始点と終了点の2つのインデックス用意する。
setに一文字ずつ記録し重複があるのかないのか管理。
TLEにはならないが、2つのループで全ての位置を試しているため時間計算量がO(n^2)、空間計算量はO(n)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

特に問題ないです。計算量は文字の種類が m とすると、O(mn) でも抑えられてしまいそうですね。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oda
レビューありがとうございます。総当たりだからすぐ「だめ」とみなす姿勢は改めないとですね。


次にsliding windowで考える。
こちらもsubstringを作るための開始点と終了点の2つのインデックス用意する。
全ての位置を試すのではなく、必要な場合のみインデックスを動かす。

重複がない間は、set内に文字を入れながら終了点を後ろにずらす。
重複があれば開始点の位置を前に進める。またsetから開始点にあった文字を削除する。

## ステップ2
・setの変数名をunique_lettersにしたが、setに対してuniqueは助長か。
setの中身は重複しないため。

・setからmapへの管理方法に変更
mapを用いて文字ごとの登場回数を記録する方法ではsetと同じような処理になる。
この場合setの方がシンプルな構造なのでsetが好み。

mapには文字ごとのindexを記録する。
これによりwhileループで開始位置を動かしていた処理をなくすことができる。

・変数名はstartとendよりleftとrightが一般的か

## ステップ3
**3回書き直しやりましょう、といっているのは、不自然なところや負荷の高いところは覚えられないからです。**

## 他の方の解法
基本方針は同じ
https://github.com/katsukii/leetcode/pull/5/commits/7dee757451b863e0166c44d43329c6863b6ac6d9
https://github.com/Yoshiki-Iwasa/Arai60/pull/42

文字コードを使って使って、インデックスを管理する方法
レビューやり取りを見ていると理解できていない箇所がちらほらあるので別途調べる
https://github.com/philip82148/leetcode-arai60/pull/3

自分もsetを使う方が好みでした。
みなさん指摘されている通りindexも管理して、それを使って範囲を狭める方が効率的ですよね。
https://github.com/fhiyo/leetcode/pull/48/commits/77b26c5a02d26112cd7ff94bf4d330afdde8a68a

## Discorなど

20 changes: 20 additions & 0 deletions 3_LongestSubstringWithoutRepeatingCharacters/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int longest_length = 0;

set<char> unique_letters;
int start = 0;
for (int end = 0; end < s.size(); end++) {
while (unique_letters.contains(s[end])) {
unique_letters.erase(s[start]);
start++;
}

unique_letters.insert(s[end]);
longest_length = max(longest_length, end - start + 1);
}

return longest_length;
}
};
21 changes: 21 additions & 0 deletions 3_LongestSubstringWithoutRepeatingCharacters/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int longest_length = 0;

map<char, int> letters_to_index;
int left = 0;
for (int right = 0; right < s.size(); right++) {
if (letters_to_index[s[right]] > 0) {
// left = letters_to_index[s[right]]だと現地点より前に
// 重複があった場合そこの位置をleftにしてしまう
left = max(letters_to_index[s[right]], left);
}
longest_length = max(longest_length, right - left + 1);
// 重複が発生した際に、その次の位置をleftにするためここで+1して記録
letters_to_index[s[right]] = right + 1;
}

return longest_length;
}
};
17 changes: 17 additions & 0 deletions 3_LongestSubstringWithoutRepeatingCharacters/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int longest_length = 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

どちらでもいいんですがmax_len(gth)という選択肢もあります(短いので)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philip82148 レビューありがとうございます。そうですね。少しlongest_lengthは長いと感じてました。。。

map<char, int> letters_to_index;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mapなら単数to単数/複数がいいと思います!letter_to_index等

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

また、今回の場合ascii128文字なので、配列にするのもありです。
そうすると-1で初期化出来て、letter_to_index[s[i]] = i;と言葉通りに出来ます。
(max()の中で1を足す)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philip82148

今回の場合ascii128文字なので、配列にするのもありです
なるほどです。綺麗な管理方法だと思いました。ありがとうございます。
下記で実装してみました🙇

104d35b

int left = 0;
for (int right = 0; right < s.size(); right++) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

僕はleftやright、begin/endが半開区間([begin, end))のイメージがあり、(もしかしたらそれを気にしないで書くことも多いのかもしれませんが)、ちょっと余計なことを考えてしまいそうなので、僕の回答では[begin, i]で書いていましたね。(どちらでもいいと思います)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@philip82148

僕はleftやright、begin/endが半開区間([begin, end))のイメージがあり

これはbinary searchの問題を解く中でleft right が必ずしも半開区間という考え方は無くなりました。

僕の回答では[begin, i]で書いていましたね。(どちらでもいいと思います)
この辺りは好みでしょうか。たくさんレビューいただきありがとうございました!

Copy link

@olsen-blue olsen-blue Mar 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ご参考までに共有ですが、リーダブルコードには、begin/endが [begin, end)半開区間、start/lastが [start, last]閉区間のイメージということが記載されていました。

if (letters_to_index[s[right]] > 0) {
left = max(letters_to_index[s[right]], left);
}
longest_length = max(longest_length, right - left + 1);
letters_to_index[s[right]] = right + 1;
}

return longest_length;
}
};
19 changes: 19 additions & 0 deletions 3_LongestSubstringWithoutRepeatingCharacters/vector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int longest_length = 0;
// ASCII文字用の配列を-1で初期化
vector<int> letters_to_index(128, -1);
int left = 0;

for (int right = 0; right < s.size(); right++) {
if (letters_to_index[s[right]] != -1) {
left = max(letters_to_index[s[right]] + 1, left);
}
longest_length = max(longest_length, right - left + 1);
letters_to_index[s[right]] = right;
}

return longest_length;
}
};