algorithm <B> data access in array 2/99
ways to access or retrieve elements from a string (or array) in C++, beyond the standard indexing:
🧩 Ways to Access Characters in a String
1️⃣ Using Indexing (Array-Style)
char c = s[right]; // most common
- Pros: Simple, concise, fast.
- Cons: No bounds check (unsafe if index is invalid).
2️⃣ Using .at()
Method
char c = s.at(right);
- Pros: Performs bounds checking → throws
std::out_of_range
if invalid. - Slightly slower than
operator[]
.
3️⃣ Using Iterators
auto it = s.begin() + right;
char c = *it;
- Pros: Works well with STL algorithms and ranges.
- Flexible if you want to iterate with iterators instead of indices.
for (auto it = s.begin(); it != s.end(); ++it) {char c = *it;
}
4️⃣ Using Range-Based For Loop
for (char c : s) {// use c directly
}
- Pros: Very clean, avoids explicit indexing.
- Limitation: You don’t get the index directly.
5️⃣ Using Pointers
const char* p = s.c_str(); // pointer to internal array
char c = *(p + right);
- Pros: Can treat string as C-style array.
- Cons: Risky if not careful; mostly used in performance-critical code.
Summary Table
Method | Index Available? | Bounds Checked? | Use Case |
---|---|---|---|
s[i] | ✅ | ❌ | Most common, simple |
s.at(i) | ✅ | ✅ | Safe access, throws exception if out-of-bounds |
iterator | ✅ (via arithmetic) | ❌ | STL-style iteration, flexible |
range-based for | ❌ | ❌ | Clean loop over all chars, no index |
pointer | ✅ (via arithmetic) | ❌ | Low-level / performance critical |
💡 Tip:
- For sliding window problems, you usually need the index →
s[i]
ors.at(i)
is best. - For simple iteration without needing the index → range-based for works great.
If you want, I can show a sliding window example rewritten using iterators instead of indices, so you can see how right
pointer can be replaced with an iterator style. This is a good alternative approach.
Great! Let’s rewrite the sliding window “longest substring without repeating characters” example using iterators instead of index-based access.
int lengthOfLongestSubstring(string s) {unordered_map<char, int> lastIndex;int maxLen = 0;auto left = s.begin(); // iterator for the start of the windowfor (auto right = s.begin(); right != s.end(); ++right) {char c = *right; // access current character// Check if c exists in the map and its last occurrence is inside the windowauto it = lastIndex.find(c);if (it != lastIndex.end() && it->second >= distance(s.begin(), left)) {// Move left iterator past the duplicateleft = s.begin() + (it->second + 1);}// Store the index of c (distance from s.begin())lastIndex[c] = distance(s.begin(), right);// Update max lengthmaxLen = max(maxLen, int(distance(left, right) + 1));}return maxLen;
}
Key Points
left
andright
are iterators instead of indices.- Use
*right
to access the character. distance(s.begin(), left/right)
converts iterator to integer index when needed for the map or calculations.- Logic of sliding window remains exactly the same, just a different way to traverse.
Pros of Iterator Style
- STL-friendly → can work with other containers, not just strings.
- Cleaner if you prefer iterator arithmetic.
Cons
- Slightly more verbose because you often need
distance()
to get the index. - Less intuitive if you’re used to
s[i]
.
If you want, I can also show a version using a range-based for loop—which completely avoids indices—so you can see all three styles side by side. This helps you decide which is best for different problems.
Perfect! Let’s compare three styles to solve the “longest substring without repeating characters” problem: index-based, iterator-based, and range-based for loop.
1️⃣ Index-Based (Most Common)
int lengthOfLongestSubstring(string s) {unordered_map<char,int> lastIndex;int left = 0, maxLen = 0;for (int right = 0; right < s.size(); right++) {char c = s[right];if (lastIndex.count(c) && lastIndex[c] >= left) {left = lastIndex[c] + 1;}lastIndex[c] = right;maxLen = max(maxLen, right - left + 1);}return maxLen;
}
Pros: Simple, fast, easy to read.
Cons: No bounds checking (use s.at(i)
if safety needed).
2️⃣ Iterator-Based
int lengthOfLongestSubstring(string s) {unordered_map<char,int> lastIndex;int maxLen = 0;auto left = s.begin();for (auto right = s.begin(); right != s.end(); ++right) {char c = *right;auto it = lastIndex.find(c);if (it != lastIndex.end() && it->second >= distance(s.begin(), left)) {left = s.begin() + (it->second + 1);}lastIndex[c] = distance(s.begin(), right);maxLen = max(maxLen, int(distance(left, right) + 1));}return maxLen;
}
Pros: STL-friendly, works with other containers.
Cons: Slightly more verbose due to distance()
conversions.
3️⃣ Range-Based For Loop
int lengthOfLongestSubstring(string s) {unordered_map<char,int> lastIndex;int left = 0, maxLen = 0;int right = 0;for (char c : s) {if (lastIndex.count(c) && lastIndex[c] >= left) {left = lastIndex[c] + 1;}lastIndex[c] = right;maxLen = max(maxLen, right - left + 1);right++;}return maxLen;
}
Pros: Very clean, minimal syntax.
Cons: You still need a separate right
counter if you want indices for the map.
✅ Takeaways
- Index-based: easiest for beginners, fastest for arrays/strings.
- Iterator-based: flexible for STL containers, useful in templates.
- Range-based for: cleanest syntax if you mostly need elements, not indices.