Skip to content

Commit 7d4491f

Browse files
committed
Leetcode maxConcatString
1 parent 1e753da commit 7d4491f

6 files changed

Lines changed: 138 additions & 8 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
.history
2-
__pycache__
2+
__pycache__
3+
temp.py
4+
input.txt
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
# 1239. Maximum Length of a Concatenated String with Unique Characters
3+
# https://leetcode.com/problems/maximum-length-of-a-concatenated-string-with-unique-characters
4+
# Dynamic programming
5+
# 17/09/2025
6+
7+
def maxLength(self, arr: list[str]) -> int:
8+
totalSubsets = 2 ** (len(arr))
9+
# dp[mask] = (Mask length with mask, caracs used)
10+
for i in range(len(arr)):
11+
s = arr[i]
12+
arr[i] = 0
13+
for carac in s:
14+
j = ord(carac) - ord("a")
15+
if arr[i] & (1 << j):
16+
# strings with duplicates are considered empty
17+
arr[i] = 0
18+
break
19+
arr[i] |= (1 << j)
20+
21+
dp = [
22+
(0, 0) for _ in range(totalSubsets)
23+
]
24+
25+
for mask in range(totalSubsets):
26+
for j in range(len(arr)):
27+
if mask == (1 << j):
28+
dp[mask] = (arr[j].bit_count(), arr[j])
29+
if mask & (1 << j):
30+
maskWithoutJ = mask & ~(1 << j)
31+
if dp[maskWithoutJ][0] > 0 and (dp[maskWithoutJ][1] & arr[j] == 0):
32+
dp[mask] = (
33+
dp[maskWithoutJ][0] + arr[j].bit_count(),
34+
dp[maskWithoutJ][1] | arr[j]
35+
)
36+
return max(dp)[0]
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
2+
3+
from utils.visualize import AlgorithmVisualizer
4+
5+
class App(AlgorithmVisualizer):
6+
def __init__(self, title = "1239. Maximum Length of a Concatenated String with Unique Characters",
7+
problem_url = "https://leetcode.com/problems/maximum-length-of-a-concatenated-string-with-unique-characters",
8+
code_url = "https://github.com/Vic-Nas/PythonSolutions/blob/main/leetcode/maxConcatString/maxConcatString.vn.py",
9+
arr = []
10+
):
11+
self.inp = arr
12+
super().__init__(title, problem_url, code_url)
13+
14+
def run_algorithm(self):
15+
inp = self.inp
16+
arr = self.inp.copy()
17+
totalSubsets = 2 ** (len(arr))
18+
for i in range(len(inp)):
19+
s = inp[i]
20+
arr[i] = 0
21+
for carac in s:
22+
j = ord(carac) - ord("a")
23+
if arr[i] & (1 << j):
24+
# strings with duplicates are considered empty
25+
arr[i] = 0
26+
break
27+
arr[i] |= (1 << j)
28+
self.add_step(
29+
variables = {
30+
"arr": inp,
31+
"Converted arr": arr
32+
}
33+
)
34+
35+
dp = [
36+
(0, 0) for _ in range(totalSubsets)
37+
]
38+
from copy import deepcopy
39+
for mask in range(totalSubsets):
40+
for j in range(len(arr)):
41+
if mask == (1 << j):
42+
dp[mask] = (arr[j].bit_count(), arr[j])
43+
if mask & (1 << j):
44+
maskWithoutJ = mask & ~(1 << j)
45+
if dp[maskWithoutJ][0] > 0 and (dp[maskWithoutJ][1] & arr[j] == 0):
46+
dp[mask] = (
47+
dp[maskWithoutJ][0] + arr[j].bit_count(),
48+
dp[maskWithoutJ][1] | arr[j]
49+
)
50+
included = [inp[i] for i in range(len(inp)) if mask & (1 << i)]
51+
step = (j, inp[j])
52+
maskSaved = (f"{mask:0{len(arr)}b}"[::-1], included)
53+
dpSaved = [
54+
(x, "".join(c for c in "abcdefghijklmnopqrstuvwxyz" if y & (1 << "abcdefghijklmnopqrstuvwxyz".index(c)))) for x, y in dp
55+
]
56+
inpSaved = deepcopy(inp)
57+
self.add_step(
58+
variables = {
59+
"inp": inpSaved,
60+
"mask": maskSaved,
61+
"j": step,
62+
"dp": dpSaved,
63+
},
64+
highlight = [mask],
65+
notToHighlight = [maskSaved, step, inpSaved]
66+
)
67+
dpSaved = [
68+
(x, "".join(c for c in "abcdefghijklmnopqrstuvwxyz" if y & (1 << "abcdefghijklmnopqrstuvwxyz".index(c)))) for x, y in dp
69+
]
70+
res = max(dp)[0]
71+
self.add_step(
72+
variables = {
73+
"final dp": dpSaved,
74+
"res = max(dp)[0]": res
75+
}
76+
)
77+
return res
78+
79+
def get_complexity(self):
80+
return {
81+
"time": "O(n * 2^n)",
82+
"space": "O(2^n)"
83+
}
84+
85+
86+
App(arr = ["cha","r","act","ers"]).show()

leetcode/nonCoprimes/nonCoprimes.vn.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11

2+
# 2197. Replace Non-Coprime Numbers in Array
3+
# https://leetcode.com/problems/replace-non-coprime-numbers-in-array
4+
# Double-linked list
5+
# 16/09/2025
26

37
def replaceNonCoprimes(self, nums: list[int]) -> list[int]:
48
from functools import cache

leetcode/nonCoprimes/nonCoprimes_vis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def ppcm(a, b): return a * b // pgcd(a, b)
4848
class App(AlgorithmVisualizer):
4949
def __init__(self, title = "Replace Non-Coprime Numbers in Array",
5050
desc = "",
51-
problem_url = "https://leetcode.com/problems/replace-non-coprime-numbers-in-array/description/",
51+
problem_url = "https://leetcode.com/problems/replace-non-coprime-numbers-in-array",
5252
code_url = "https://github.com/Vic-Nas/PythonSolutions/blob/main/leetcode/nonCoprimes/nonCoprimes.vn.py",
5353
nums = []):
5454
self.res = DoubleLinkedList(nums)

utils/visualize.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ def add_step(self,
123123
variables: Dict[str, Any],
124124
message: str = "",
125125
highlight: List[int] = None,
126-
current: List[int] = None):
126+
current: List[int] = None,
127+
notToHighlight = []):
127128
"""
128129
Save a step in your algorithm for visualization.
129130
@@ -160,9 +161,10 @@ def add_step(self,
160161
step_data = {
161162
'variables': variables.copy(),
162163
'message': message,
163-
'highlight': highlight.copy(),
164+
'highlight': highlight,
164165
'current': current.copy(),
165-
'step_number': len(self.steps)
166+
'step_number': len(self.steps),
167+
"notToHighlight": notToHighlight,
166168
}
167169
self.steps.append(step_data)
168170

@@ -220,7 +222,7 @@ def save(self, filename: str) -> str:
220222
print(f"💾 Visualization saved to {filename}")
221223
return filename
222224

223-
def _render_variable(self, name: str, value: Any, highlight: List[int] = None, current: List[int] = None) -> str:
225+
def _render_variable(self, name: str, value: Any, highlight: List[int] = None, current: List[int] = None, notToHighlight = []) -> str:
224226
"""Convert a variable to HTML representation."""
225227
if highlight is None:
226228
highlight = []
@@ -229,7 +231,7 @@ def _render_variable(self, name: str, value: Any, highlight: List[int] = None, c
229231

230232
# Handle different data types
231233
if isinstance(value, (list, tuple)):
232-
return self._render_array(name, value, highlight, current)
234+
return self._render_array(name, value, highlight if value not in notToHighlight else [], current)
233235
elif isinstance(value, dict):
234236
return self._render_dict(name, value)
235237
elif isinstance(value, (int, float, str, bool)):
@@ -295,7 +297,7 @@ def _generate_html(self) -> str:
295297
# Render all variables in this step
296298
variables_html = []
297299
for var_name, var_value in step['variables'].items():
298-
var_html = self._render_variable(var_name, var_value, step['highlight'], step['current'])
300+
var_html = self._render_variable(var_name, var_value, step['highlight'], step['current'], notToHighlight=step['notToHighlight'])
299301
variables_html.append(var_html)
300302

301303
js_steps.append({

0 commit comments

Comments
 (0)