LeetCode #49 – Group Anagrams | Python Implementation I implemented a character frequency signature approach to group anagrams efficiently. Instead of sorting each string (which would cost O(k log k) per string), I generate a fixed-size frequency array of 26 elements representing counts for each letter. This array is converted to a tuple to serve as a hashable key in a dictionary, mapping each unique signature to its corresponding anagram group. This pattern is widely used in document clustering, duplicate detection systems, and search indexing pipelines. res = defaultdict(list) for s in strs: count = [0] * 26 # Fixed-size frequency array for c in s: count[ord(c) - ord("a")] += 1 # Map 'a'-'z' to indices 0-25 res[tuple(count)].append(s) # Use tuple as hashable key return list(res.values()) # Return grouped anagrams Key Takeaway: Using character frequency arrays as keys avoids the sorting overhead entirely. The tuple conversion is crucial since lists aren't hashable in Python. This technique shines when dealing with large datasets where minimizing per-element processing time is critical. Time: O(n * k) where n is the number of strings and k is the max string length | Space: O(n * k) #LeetCode #DataStructures #Python #HashMap #Anagrams #CodingInterview #ProblemSolving #SoftwareEngineering
Python LeetCode Solution: Grouping Anagrams with Frequency Arrays
More Relevant Posts
-
🧠 Python Concept That Explains Attribute Lookup Order: Descriptor Lookup Chain When you access: obj.attr Python follows a precise order 👀 🔍 The Real Lookup Order Python checks in this exact sequence: 1️⃣ Data descriptor (__set__ / __delete__) 2️⃣ Instance __dict__ 3️⃣ Non-data descriptor (__get__) 4️⃣ Class __dict__ 5️⃣ __getattr__ 🧪 Example Insight class D: def __get__(self, obj, owner): return "descriptor" class C: x = D() c = C() c.x = "instance" print(c.x) ✅ Output instance Because instance dict beats non-data descriptor. 🧒 Explain Like I’m 5 Imagine looking for a toy 🧸 1️⃣ Guard holding it 2️⃣ Your bag 3️⃣ Shelf 4️⃣ Store 5️⃣ Ask someone That order = lookup chain. 💡 Why This Matters ✔ Descriptor behavior ✔ Properties ✔ ORMs ✔ Framework internals ✔ Debugging weird attribute bugs ⚡ Key Rule 🐍 Data descriptors override instance attributes. 🐍 Non-data descriptors don’t. 🐍 Attribute access in Python isn’t random. 🐍 It follows a precise chain 🐍 Understanding the descriptor lookup order explains many “magic” behaviors. #Python #PythonTips #PythonTricks #AdvancedPython #CleanCode #LearnPython #Programming #DeveloperLife #DailyCoding #100DaysOfCode
To view or add a comment, sign in
-
-
🐍 Python Tip: Stop copying lists the wrong way! Every Python developer hits this bug at some point: a = [1, 2, 3, 4] b = a b.append(5) print(a) # [1, 2, 3, 4, 5] 😱 When you write b = a, you're NOT copying the list. You're just creating another variable pointing to the SAME memory (call by reference). ✅ The 3 ways to properly copy a list: • b = a[:] → Slice copy • b = a.copy() → Built-in method • b = list(a) → Constructor copy ⚡ Which one is fastest? a[:] wins — it's a direct C-level memory slice with zero Python overhead. Speed ranking: a[:] > a.copy() > list(a) ⚠️ BUT — all 3 are SHALLOW copies. If your list contains nested lists or objects, changes will still bleed through: a = [[1, 2], [3, 4]] b = a.copy() b[0].append(99) print(a) # [[1, 2, 99], [3, 4]] 😬 For nested structures, use deepcopy: import copy b = copy.deepcopy(a) Quick rule: 📦 Flat list → a[:] or a.copy() 📦 Nested list → copy.deepcopy(a) Small detail. Big bugs if you get it wrong. ♻️ Repost if this helped someone on your team! #Python #Programming #SoftwareDevelopment #CodingTips #PythonTips
To view or add a comment, sign in
-
-
Python Study Day 4 - String Manipulation Strings are sequences of characters. Python provides many useful methods to manipulate strings. - Common String Operations: Concatenation: Joining two or more strings together using the + operator. first_name = "John" last_name = "Doe" full_name = first_name + " " + last_name print(full_name) # Output: "John Doe" Repetition: Repeating a string multiple times using the * operator. greeting = "Hello! " * 3 print(greeting) # Output: "Hello! Hello! Hello! " - String Methods: upper(): Converts a string to uppercase. lower(): Converts a string to lowercase. strip(): Removes leading and trailing spaces from a string. replace(old, new): Replaces a substring with another string. message = " Hello, World! " print(message.strip()) # Output: "Hello, World!" print(message.upper()) # Output: "HELLO, WORLD!" print(message.replace("World", "Python")) # Output: "Hello, Python!" - Accessing String Characters: You can access individual characters in a string using indexing. Python uses zero-based indexing, so the first character has an index of 0. text = "Python" print(text[0]) # Output: P print(text[2]) # Output: t You can also use negative indexing to start counting from the end of the string. print(text[-1]) # Output: n print(text[-3]) # Output: h - Slicing Strings: You can extract a portion (substring) of a string using slicing. text = "Python Programming" print(text[0:6]) # Output: Python (extracts from index 0 to 5) print(text[:6]) # Output: Python (same as above) print(text[7:]) # Output: Programming (from index 7 to the end)
To view or add a comment, sign in
-
99% of Python developers don't know about __slots__. 𝗕𝘂𝘁 𝘁𝗵𝗶𝘀 𝘀𝗶𝗻𝗴𝗹𝗲 𝗹𝗶𝗻𝗲 𝗰𝗮𝗻 𝗰𝘂𝘁 𝘆𝗼𝘂𝗿 𝗺𝗲𝗺𝗼𝗿𝘆 𝘂𝘀𝗮𝗴𝗲 𝗯𝘆 𝟰𝟬-𝟲𝟬%. Here's why this matters in ML/AI applications: 𝗪𝗶𝘁𝗵𝗼𝘂𝘁 __𝘀𝗹𝗼𝘁𝘀__: class DataPoint: def __init__(self, x, y, features): self.x = x self.y = y self.features = features 𝗘𝗮𝗰𝗵 𝗶𝗻𝘀𝘁𝗮𝗻𝗰𝗲 𝘀𝘁𝗼𝗿𝗲𝘀 𝗮𝘁𝘁𝗿𝗶𝗯𝘂𝘁𝗲𝘀 𝗶𝗻 𝗮 𝗱𝗶𝗰𝘁𝗶𝗼𝗻𝗮𝗿𝘆 → ~𝟮𝟴𝟬 𝗯𝘆𝘁𝗲𝘀 𝗽𝗲𝗿 𝗼𝗯𝗷𝗲𝗰𝘁 𝗪𝗶𝘁𝗵 __𝘀𝗹𝗼𝘁𝘀__: class DataPoint: __slots__ = ['x', 'y', 'features'] def __init__(self, x, y, features): self.x = x self.y = y self.features = features 𝗔𝘁𝘁𝗿𝗶𝗯𝘂𝘁𝗲𝘀 𝘀𝘁𝗼𝗿𝗲𝗱 𝗶𝗻 𝗳𝗶𝘅𝗲𝗱 𝘀𝘁𝗿𝘂𝗰𝘁𝘂𝗿𝗲 → ~𝟭𝟮𝟬 𝗯𝘆𝘁𝗲𝘀 𝗽𝗲𝗿 𝗼𝗯𝗷𝗲𝗰𝘁 Real impact on ML workflows: • Training with 1M+ data points? Save ~160MB instantly • Faster attribute access (15-20% speed boost) • Cleaner memory profiling during model training 𝗧𝗵𝗲 𝗰𝗮𝘁𝗰𝗵? → No dynamic attribute addition → Inheritance becomes trickier → Can't use with multiple inheritance easily When building ML pipelines with massive datasets, this optimization can be the difference between smooth training and memory crashes. Have you used __slots__ in your Python projects? What memory optimization tricks do you swear by? 🔧 #Python #MachineLearning #PerformanceOptimization
To view or add a comment, sign in
-
-
🧠 Python Concept That Explains Why += Can Mutate: In-place vs New Objects (__iadd__) Why does this behave differently? 👀 a = [1, 2] b = a a += [3] print(a) # [1, 2, 3] print(b) # [1, 2, 3] But: x = (1, 2) y = x x += (3,) print(x) # (1, 2, 3) print(y) # (1, 2) Same += … different result 🤯 🤔 The Reason: __iadd__ Python tries: 1️⃣ __iadd__ (in-place add) 2️⃣ else → __add__ (new object) 🧪 Lists implement __iadd__ list.__iadd__(self, other) So list is modified in place. 🧪 Tuples don’t So Python creates a new tuple. 🧒 Simple Explanation List = clay 🧱 You reshape same clay. Tuple = brick 🧱 You must make new brick. 💡 Why This Matters ✔ Mutability understanding ✔ Side-effects bugs ✔ Performance ✔ Data structures ✔ Interview classic ⚡ Key Insight id(a) == id(a += ...) True for mutable types False for immutable types 💻 In Python, += doesn’t always mean “new value”. 🐍 Sometimes it means “modify in place” 🐍 The difference comes from __iadd__. #Python #PythonTips #PythonTricks #AdvancedPython #CleanCode #LearnPython #Programming #DeveloperLife #DailyCoding #100DaysOfCode
To view or add a comment, sign in
-
-
Consider the following code in Python: def add_item(lst): lst.append(100) a = [1, 2, 3] add_item(a) print(a) What happens here? The correct explanation is: ✅ An in-place modification occurs on the list. Lists in Python are mutable objects, which means they can be modified after they are created. Let’s break it down step by step. 1️⃣ Creating the list When we write: a = [1, 2, 3] Python creates a list object in memory, and the variable a references it: a → [1, 2, 3] 2️⃣ Calling the function When the function is called: add_item(a) The parameter lst inside the function now references the same list object: a → [1, 2, 3] lst → ↑ (same list) ➡️ Both variables point to the same object in memory. 3️⃣ Inside the function Inside the function we execute: lst.append(100) The append() method modifies the list itself. This is called in-place modification, meaning the original list object is updated instead of creating a new one. The list now becomes: [1, 2, 3, 100] 4️⃣ Printing the result Since both a and lst reference the same list, the change is visible through a. Now when we execute: print(a) Output: [1, 2, 3, 100] 📌 Final thought Understanding how variables reference objects in memory is essential when working with mutable data types like lists in Python. #Python #PythonProgramming #Coding #LearnPython #SoftwareDevelopment
To view or add a comment, sign in
-
Python Default Arguments 🐍🐍 Default Arguments Powerful but Dangerous. Let’s see why. Basic Example def greet(name="Guest"): print(f"Hello {name}") greet() # Hello Guest greet("Rahul") # Hello Rahul #Looks simple. But here is the important detail: 👉 Default arguments are evaluated only once — when the function is defined, not when it is called. The Classic Python Trap def add_item(item, items=[]): items.append(item) return items print(add_item(1)) # [1] print(add_item(2)) # [1, 2] print(add_item(3)) # [1, 2, 3] Most people expect: [1] [2] [3] But Python keeps reusing the same list object. Why? Because the default list "[]" was created once at function definition time and reused in every call. The Correct Way def add_item(item, items=None): if items is None: items = [] items.append(item) return items Now every call gets a fresh list. Why Python Works This Way Python stores default arguments inside the function object: function.__defaults__ They are not recreated each time the function runs. This design improves performance but can introduce subtle bugs. #Takeaway ✔ Default arguments are evaluated once at definition time ✔ Mutable defaults ("list", "dict", "set") can cause shared state bugs ✔ Use "None" as a safe default when mutation is involved #Python #PythonTips #Programming #SoftwareEngineering #PythonDeveloper
To view or add a comment, sign in
-
-
One rule that saves a lot of bugs in Python: input() always gives you a string. So age = input("Age? ") then age + 5 will error until you do age = int(age). Type conversion is everywhere: string → int for math, int → str for printing, and knowing what each of int(), float(), complex(), bool(), and str() accepts (and what they don’t). I put together a complete guide: explicit vs implicit, all five functions, a “what can convert to what” table, rules, mistakes, and exercises. Read it here: https://lnkd.in/dqSMhkpi #Python #Coding #Developer
To view or add a comment, sign in
-
💡 How to split text into words in Python... Without having incorrect results because of punctuation. When you need to split text into words, the typical solution of using the string method `split` will produce words with adjacent punctuation: ```py text = "Hello, there!" print(text.split()) # ['Hello,', 'there!'] ``` A more robust approach uses the regular expression function `re.split` and the special regex character `\W`: ```py import re print(re.split(r"\W+", text)) # ['Hello', 'there', ''] ``` The character `\W` matches non-word characters, so your final list will only contain words strings: strings of alphanumeric characters. You can tweak the regular expression to match your expectation of what a _word_ must be. The final empty string `''` shows up because the original text ends with a separator.
To view or add a comment, sign in
-
-
⚠️ Python Surprise: An error doesn’t undo the change Check out this snippet. At first glance, it looks like a guaranteed "no-op" because we know tuples are immutable: x = (1, 2, [3, 4]) try: x[2] += [5, 6] except TypeError as e: print(f"Error: {e}") print(f"Tuple after error: {x}") Now checkout the output Error: 'tuple' object does not support item assignment Tuple after error: (1, 2, [3, 4, 5, 6]) The error happened, but the list was still updated What's really happening? In Python, x[2] += [5, 6] isn't one atomic action. It’s actually two separate operations: ✅ Step 1 — Mutation Python updates the list in place. [3, 4] → [3, 4, 5, 6] ✔ success ❌ Step 2 — Assignment Python tries to put the updated list back into the tuple. Tuple says: “Nope. I’m immutable.” → TypeError But by then… the mutation already happened. Python doesn't have a "transactional rollback" for this. This can lead to some nasty bugs if you’re not careful with mutable objects inside "immutable" containers. Have you ever run into a Python behavior that felt like it was breaking its own rules? Let’s hear it in the comments! #python #PythonProgramming #coding #LearnPython
To view or add a comment, sign in
-
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development