Instello
1/12
Today
NeetCode 75
Longest Palindromic Substring: expand around every center
longest mirror inside?
swipe →
The problem
Given a string, return the longest contiguous substring that is a palindrome (reads the same both ways). "babad" -> "bab" ("aba" is also valid, same length). "cbbd" -> "bb". Substring means a continuous slice — not a subsequence.
babad
The idea: grow from the center
Every palindrome has a center and mirrors outward from it. So instead of checking all substrings, stand at a center and expand: compare the chars just left and right; while they match you have a bigger palindrome. Two kinds of center: - a single char (odd length, like 'aba') - a gap between two chars (even length, like 'bb').
Expanding, step by step
From a center, set l and r and push them outward while s[l]==s[r] and you stay in bounds. 'babad', odd center at index 2 ('b'): l=2,r=2 -> 'b' l=1,r=3 -> 'a'=='a', grow -> 'aba' l=0,r=4 -> 'b' vs 'd', stop. Found 'aba', length 3.
grew to aba
Cover both center types
For each index i, expand twice: - odd: center (i, i) - even: center (i, i+1) Keep the longest palindrome any expansion produces. n characters give about 2n centers, so you never miss an answer.
Code
After the while loop l and r have each stepped one too far, so the valid window is s[l+1:r].
``` def longestPalindrome(s): res = "" def expand(l, r): while l >= 0 and r < len(s) and s[l] == s[r]: l -= 1 r += 1 return s[l + 1:r] for i in range(len(s)): for cand in (expand(i, i), expand(i, i + 1)): if len(cand) > len(res): res = cand return res ```
Complexity + traps
Time: O(n^2) — 2n centers, each expands up to O(n). Space: O(1) extra (ignoring the output string). Traps: - Don't forget even-length centers; checking only single-char centers misses 'bb'. - After the loop, l and r overshoot by one, so the answer is s[l+1:r]. - Brute force (test every substring) is O(n^3); expand-around-center is the clean O(n^2). Manacher's gets O(n) but is rarely needed. Siblings: Palindromic Substrings, Longest Common Substring.
recall
Why expand from both (i, i) and (i, i+1)?
Know it cold?
Stand at each of the 2n centers and expand while the mirror holds. O(n^2), O(1) space.
#neetcode75 #string
10,000 GitHub repos, one malware playbook — how did they hide it in plain sight?
swipe →
WHAT HAPPENED
A report claimed to find 10,000 GitHub repositories that were distributing Trojan malware. The key pattern was not random spam: the repos were said to have different names and contributors, and to follow a repeatable structure that could be detected automatically.
THE CLONE PATTERN
The author said they first noticed duplicated repositories while searching GitHub project names through Google and Bing. Some repos looked like copied versions of others: same name, same description, copied commit history, and then a later commit that only changed the README, which is the top-level text file that usually explains a project.
WHAT CHANGED IN THE REPO
According to the report, the repos kept deleting the previous commit and pushing an identical new one every few hours. The only meaningful change was a new link in the README to a ZIP archive, which is a compressed file bundle that can hide multiple files inside one download.
WHAT THE ZIP CONTAINED
The archive reportedly contained four files: `Application.cmd` or `Launcher.cmd`, an executable such as `loader.exe` or `luajit.exe`, a file named like `random_name.cso` or `random_name.txt`, and `lua51.dll`. In plain terms, that means a script, a program that can run directly on Windows, a supporting data file, and a DLL, which is a shared library file used by programs.
```
Example of the file bundle the report described
files = [ 'Application.cmd', 'loader.exe', 'random_name.cso', 'lua51.dll', ] ```
HOW THE AUTHOR FOUND THEM
The author said they used gharchive, a public archive of GitHub events, to filter recent pushes, then used the GitHub API, which is the programmatic way to query GitHub data. They noted the API limit of 5,000 requests per hour and said they could not inspect all 500 million repositories directly, so they narrowed the search with timing and commit-pattern filters.
WHY THAT MATTERED
The reported numbers changed as the author widened the search: about 3,000 repos matched one early filter, then 14 matched a tighter pass, and later 40,000 matched a broader activity window, with 10,000 matching the suspected malware pattern exactly. The author also said the repos had been around for months or more than a year and were not automatically removed by GitHub.
WHY IT'S INTERESTING
Commenters argued this may be more than just repository spam, because GitHub search visibility can help malware land in front of people who are looking for software. The report also suggested the attackers may have used tags, copied history, and repeated commits to look trustworthy and possibly test or evade detection systems.
WHAT THE COMMUNITY SAID
Commenters shared different angles: astronodev said their VirusTotal analysis showed network behavior that looked like crypto-theft infrastructure; mmsc said GitHub removed one similar report quickly but another took much longer; jp0001 said a sample looked related to the Disco trojan family; and emodendroket argued that open source does not make binaries safe, because many users do not verify that downloaded executables match the source code.
recall
If you found a GitHub repo that looks active but keeps rewriting the same README and linking to a ZIP, what would you check before trusting the download?
Know it cold?
github malware repos A report claimed 10,000 GitHub repos were being used to distribute Trojan malware through repeated README updates and ZIP links. The core risk is not just malicious code, but the trust users place in GitHub search results and repository appearance.
#github #malware #infosec #opensource #cybersecurity #trojan
NeetCode 75
Product Except Self — no division allowed.
answer[i] = product of all the others
swipe →
The problem
Return an array where answer[i] is the product of every element EXCEPT nums[i] — and you may not use division. For [1,2,3,4] the answer is [24,12,8,6].
without dividing the total product
Split it: left × right
The product of everything except i is (product of everything to its LEFT) × (product of everything to its RIGHT). Compute those two pieces and multiply.
answer[2] = (1·2) · (4) = 8
Two sweeps
First pass left→right: fill each slot with the running product of everything before it. Second pass right→left: multiply in the running product of everything after it. Done in O(n) with no extra array (the output doubles as scratch).
prefix pass, then suffix pass
The code
One output array, two directional sweeps.
``` def product_except_self(nums): n = len(nums) res = [1] * n prefix = 1 for i in range(n): res[i] = prefix; prefix *= nums[i] # product to the left suffix = 1 for i in range(n - 1, -1, -1): res[i] *= suffix; suffix *= nums[i] # product to the right return res ```
Cost and cousins
O(n) time, O(1) extra space (besides the output). The 'prefix then suffix sweep' idea also appears in Trapping Rain Water and Maximum Product Subarray.
recall
Why does using the output array itself for the prefix pass let you hit O(1) extra space — where does the suffix product get stored?
1 like
Know it cold?
product of array except self answer[i] = (product of everything to its left) × (product to its right). One left→right sweep fills the prefixes, one right→left sweep multiplies in the suffixes. O(n), no division.
#neetcode75 #arrays #prefix-product
complexity check
What's the time complexity?
``` def f(n): total = 0 i = 1 while i < n: for j in range(i): total += 1 i *= 2 return total ```
Know it cold?
complexity Doubling loop + inner work = geometric series. The sum is dominated by the last term.
#bigO #complexity #complexitycheck
NeetCode 75
Find the 3rd smallest value in a BST without sorting anything.
a binary search tree
swipe →
The problem, in plain words
You are given the root of a binary search tree (BST) and a number k. Return the kth smallest value in the tree, counting from 1 (so k = 1 is the very smallest). Recall a BST keeps smaller values to the left and larger values to the right of every node.
what is the k-th smallest value here?
One tiny example
Our tree holds the values 2, 3, 4, 5, 7, 8, 9. With k = 3 the answer is 4, because in sorted order (2, 3, 4, ...) the third value is 4. The question is how to reach 4 without dumping everything into a list and sorting it.
sorted: 2,3,4,5,7,8,9 → 3rd is 4
The key property: inorder is sorted
An inorder traversal visits left subtree, then the node, then right subtree. On a BST this visits the values in strictly increasing order, for free. So the kth node you visit inorder is exactly the kth smallest value. No separate sort needed, the tree is already organized.
inorder visit order = sorted rank
Walk inorder, count to k
Do an inorder traversal and keep a counter. Each time you finish going as far left as possible and process a node, decrement k. The moment k hits 0, that node is your answer, and you can stop early without touching the rest of the tree.
visit 2, then 3, then 4 → stop
The iterative code
Use an explicit stack. Push nodes while walking left to reach the smallest. Pop a node (that is the next value in sorted order), decrement k, and if k is 0 return its value. Otherwise step once to the right and repeat. The stack lets us pause and resume the traversal cleanly.
``` def kthSmallest(root, k): stack = [] cur = root while stack or cur: while cur: stack.append(cur) cur = cur.left cur = stack.pop() k -= 1 if k == 0: return cur.val cur = cur.right ```
Cost of the approach
Time is O(h + k): you descend the height h to reach the smallest value, then pop k nodes. In the worst case (k near n) that is O(n). Space is O(h) for the stack. Beats the naive 'store all values then sort', which is O(n log n) time and O(n) space and ignores the BST structure entirely.
stop at the k-th pop, skip the rest
Traps and siblings
Traps: forgetting k is 1-indexed (off-by-one); recomputing from scratch if asked repeatedly (if the tree changes a lot, augment each node with its subtree size for O(h) queries); doing a full traversal instead of stopping early. Siblings: Validate BST and Binary Tree Inorder Traversal (same inorder-is-sorted idea), BST Iterator.
inorder + early stop
recall
If an interviewer asks for the kth LARGEST value in a BST instead, what small change to the inorder traversal gives it to you directly?
Know it cold?
kth smallest element in bst Inorder traversal of a BST yields values in sorted order, so the kth node visited is the kth smallest. Stop the moment your counter hits k. O(h + k) time, O(h) space.
#neetcode75 #tree #bst #inorder #dfs
Git ignores aren’t just one file — can you tell which level is hiding your file?
swipe →
WHAT HAPPENED
A Hacker News post pointed out that Git can ignore files at three different levels, not just through the familiar `.gitignore`. The three places are: `.gitignore` in the repository, `.git/info/exclude` inside the repo’s local `.git` directory, and `~/.config/git/ignore` as a global ignore file for one user or machine. The article also showed how to change the global ignore path with `git config --global core.excludesFile ~/.gitignore_global` and how to remove that setting with `git config --global --unset core.excludesFile`.
WHAT HAPPENED
The post highlighted `git check-ignore -v`, a Git command that tells you which ignore rule is responsible for hiding a path. `-v` means verbose, which here means “show the source of the rule.” The examples showed the same file, like `.DS_Store`, being ignored depending on whether the rule came from `.gitignore`, `.git/info/exclude`, the default global ignore file, or a custom global ignore file. If nothing is ignoring the file, the command prints nothing.
WHY IT MATTERS
This is useful because each ignore level solves a different problem. `.gitignore` is for rules everyone on the project should share, `.git/info/exclude` is for repo-specific personal clutter that should stay local, and the global ignore file is for machine-specific junk that shows up everywhere, like `.DS_Store`. Think of it like three baskets: shared, repo-local, and personal.
WHY IT MATTERS
A junior developer may not know that Git ignores are not all committed the same way. `.gitignore` is versioned, which means it travels with the code; `.git/info/exclude` is not versioned, so it stays on your machine; and the global ignore affects all repositories for one user. The practical value is that you can keep local-only files out of shared project rules instead of asking everyone else to accept your personal cleanup.
WHY THE COMMUNITY CARED
Commenters mostly treated the post as a reminder of existing Git documentation, but some found the workflow tips genuinely useful. Hendrikto criticized it as a very low-effort rehash of the official docs, while bryancoxwell said they use `.git/info/exclude` heavily for things like scripts or Makefiles they want only locally. hk1337 argued that `~/.config/git/ignore` and `~/.config/git/config` are the modern global locations, and noted that `.git/info/exclude` is less common because it is per-repo and not committed.
WHY THE COMMUNITY CARED
Other commenters shared small personal patterns that made the tip feel practical. judofyr said they add `attic` to the global ignore so any repository can have an `attic/` folder for random uncommitted stuff. jeremyscanvic said the biggest new thing for them was `git check-ignore -v`, even though they already knew about the other ignore locations.
OUR TAKE
For working developers, the real lesson is to pick the smallest ignore scope that fits the problem. Shared junk belongs in `.gitignore`, personal repo clutter belongs in `.git/info/exclude`, and machine-wide noise belongs in the global ignore file. If a file disappears unexpectedly, `git check-ignore -v` is the fastest way to ask Git, “who hid this?”
recall
If a file is being ignored and you want to know why, which Git command would you run to find the exact rule and file that caused it?
Know it cold?
git ignore Git can ignore files at three levels: shared, repo-local, and global. Use the narrowest one that fits, and use `git check-ignore -v` when you need to trace the rule.
#git #gitignore #devtools #workflow #hackernews