Skip to content

Commit 3ed6855

Browse files
committed
Improved task 3435
1 parent e189bd7 commit 3ed6855

File tree

1 file changed

+74
-155
lines changed
  • src/main/kotlin/g3401_3500/s3435_frequencies_of_shortest_supersequences

1 file changed

+74
-155
lines changed
Lines changed: 74 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,184 +1,103 @@
11
package g3401_3500.s3435_frequencies_of_shortest_supersequences
22

33
// #Hard #Array #String #Bit_Manipulation #Graph #Enumeration #Topological_Sort
4-
// #2025_01_26_Time_385_(100.00%)_Space_54.41_(100.00%)
4+
// #2025_01_29_Time_35_(100.00%)_Space_43.62_(100.00%)
55

66
class Solution {
7-
private fun buildWordMap(words: List<String>): MutableMap<String?, Boolean?> {
8-
val mp: MutableMap<String?, Boolean?> = HashMap<String?, Boolean?>()
9-
for (x in words) {
10-
mp.put(x, true)
11-
}
12-
return mp
13-
}
7+
private var m = 0
8+
private var forcedMask = 0
9+
private lateinit var adj: IntArray
10+
private val idxToChar = CharArray(26)
11+
private val charToIdx = IntArray(26)
12+
private val used = BooleanArray(26)
1413

15-
private fun buildCharMap(words: List<String>): MutableMap<Char?, Boolean?> {
16-
val mp2: MutableMap<Char?, Boolean?> = HashMap<Char?, Boolean?>()
17-
for (x in words) {
18-
mp2.put(x[0], true)
19-
mp2.put(x[1], true)
14+
fun supersequences(words: Array<String>): MutableList<MutableList<Int?>?> {
15+
charToIdx.fill(-1)
16+
for (w in words) {
17+
used[w[0].code - 'a'.code] = true
18+
used[w[1].code - 'a'.code] = true
2019
}
21-
return mp2
22-
}
23-
24-
private fun initializeAnswerArray(mp: MutableMap<String?, Boolean?>, mp2: MutableMap<Char?, Boolean?>): IntArray {
25-
val tans = IntArray(26)
26-
tans.fill(0)
27-
var c = 'a'
28-
while (c <= 'z') {
29-
val aux = "" + c + c
30-
if (mp.containsKey(aux)) {
31-
tans[c.code - 'a'.code] = 2
32-
} else if (mp2.containsKey(c)) {
33-
tans[c.code - 'a'.code] = 1
20+
// Map each used letter to an index [0..m-1]
21+
for (c in 0..25) {
22+
if (used[c]) {
23+
idxToChar[m] = (c + 'a'.code).toChar()
24+
charToIdx[c] = m++
3425
}
35-
c++
3626
}
37-
return tans
38-
}
39-
40-
private fun filterWords(words: List<String>, tans: IntArray): MutableList<String> {
41-
val wtc: MutableList<String> = ArrayList<String>()
42-
for (x in words) {
43-
if (tans[x[0].code - 'a'.code] != 2 && tans[x[1].code - 'a'.code] != 2) {
44-
wtc.add(x)
45-
}
46-
}
47-
return wtc
48-
}
49-
50-
private fun updateBoundaries(wtc: MutableList<String>, bg: IntArray, ed: IntArray) {
51-
for (i in wtc.indices) {
52-
val l = wtc[i][0].code - 'a'.code
53-
if (bg[l] == -1) {
54-
bg[l] = i
27+
adj = IntArray(m)
28+
// Build graph and record forced duplicates
29+
for (w in words) {
30+
val u = charToIdx[w[0].code - 'a'.code]
31+
val v = charToIdx[w[1].code - 'a'.code]
32+
if (u == v) {
33+
forcedMask = forcedMask or (1 shl u)
34+
} else {
35+
adj[u] = adj[u] or (1 shl v)
5536
}
56-
ed[l] = i
5737
}
58-
}
59-
60-
private fun findMinimalSolutions(
61-
wtc: MutableList<String>,
62-
tans: IntArray,
63-
bg: IntArray,
64-
ed: IntArray,
65-
): MutableList<Int> {
66-
val ns: MutableList<Int> = ArrayList<Int>()
67-
for (i in 0..25) {
68-
if (tans[i] == 1) {
69-
ns.add(i)
38+
// Try all supersets of forcedMask; keep those that kill all cycles
39+
var best = 9999
40+
val goodSets: MutableList<Int?> = ArrayList<Int?>()
41+
for (s in 0..<(1 shl m)) {
42+
if ((s and forcedMask) != forcedMask) {
43+
continue
7044
}
71-
}
72-
val gm: MutableList<Int> = ArrayList<Int>()
73-
for (i in 0..<(1 shl ns.size)) {
74-
if (isValidSolution(i, ns, wtc, tans.clone(), bg, ed)) {
75-
gm.add(i)
45+
val size = Integer.bitCount(s)
46+
if (size <= best && !hasCycle(s)) {
47+
if (size < best) {
48+
best = size
49+
goodSets.clear()
50+
}
51+
goodSets.add(s)
7652
}
7753
}
78-
return gm
79-
}
80-
81-
private fun isValidSolution(
82-
i: Int,
83-
ns: MutableList<Int>,
84-
wtc: MutableList<String>,
85-
tans: IntArray,
86-
bg: IntArray,
87-
ed: IntArray,
88-
): Boolean {
89-
val indg = IntArray(26)
90-
indg.fill(0)
91-
for (j in ns.indices) {
92-
if ((i and (1 shl j)) != 0) {
93-
tans[ns[j]] = 2
94-
} else {
95-
tans[ns[j]] = 1
54+
// Build distinct freq arrays from these sets
55+
val seen: MutableSet<String?> = HashSet<String?>()
56+
val ans: MutableList<MutableList<Int?>?> = ArrayList<MutableList<Int?>?>()
57+
for (s in goodSets) {
58+
val freq = IntArray(26)
59+
for (i in 0..<m) {
60+
freq[idxToChar[i].code - 'a'.code] = if ((s!! and (1 shl i)) != 0) 2 else 1
9661
}
97-
}
98-
for (w in wtc) {
99-
if (tans[w[0].code - 'a'.code] != 2 && tans[w[1].code - 'a'.code] != 2) {
100-
indg[w[1].code - 'a'.code]++
62+
val key = freq.contentToString()
63+
if (seen.add(key)) {
64+
val tmp: MutableList<Int?> = ArrayList<Int?>()
65+
for (f in freq) {
66+
tmp.add(f)
67+
}
68+
ans.add(tmp)
10169
}
10270
}
103-
return processIndegrees(indg, tans, wtc, bg, ed)
71+
return ans
10472
}
10573

106-
private fun processIndegrees(
107-
indg: IntArray,
108-
tans: IntArray,
109-
wtc: MutableList<String>,
110-
bg: IntArray,
111-
ed: IntArray,
112-
): Boolean {
113-
val chk: MutableList<Int> = ArrayList<Int>()
114-
for (j in 0..25) {
115-
if (indg[j] == 0 && tans[j] == 1) {
116-
chk.add(j)
74+
private fun hasCycle(mask: Int): Boolean {
75+
val color = IntArray(m)
76+
for (i in 0..<m) {
77+
if (((mask shr i) and 1) == 0 && color[i] == 0 && dfs(i, color, mask)) {
78+
return true
11779
}
11880
}
119-
while (chk.isNotEmpty()) {
120-
val u: Int = chk.removeAt(chk.size - 1)
121-
if (bg[u] == -1) {
122-
continue
123-
}
124-
for (j in bg[u]..ed[u]) {
125-
val l = wtc[j][1].code - 'a'.code
126-
if (tans[l] == 2) {
127-
continue
128-
}
129-
indg[l]--
130-
if (indg[l] == 0) {
131-
chk.add(l)
132-
}
133-
}
134-
}
135-
return indg.max() == 0
81+
return false
13682
}
13783

138-
fun supersequences(wordsArray: Array<String>): List<List<Int>> {
139-
val words = wordsArray.sorted()
140-
val bg = IntArray(26)
141-
bg.fill(-1)
142-
val ed = IntArray(26)
143-
ed.fill(0)
144-
val mp = buildWordMap(words)
145-
val mp2 = buildCharMap(words)
146-
val tans = initializeAnswerArray(mp, mp2)
147-
val wtc = filterWords(words, tans)
148-
updateBoundaries(wtc, bg, ed)
149-
val ans: MutableList<MutableList<Int>> = ArrayList<MutableList<Int>>()
150-
if (wtc.isEmpty()) {
151-
val tansList: MutableList<Int> = ArrayList<Int>()
152-
for (t in tans) {
153-
tansList.add(t)
84+
private fun dfs(u: Int, color: IntArray, mask: Int): Boolean {
85+
color[u] = 1
86+
var nxt = adj[u]
87+
while (nxt != 0) {
88+
val v = Integer.numberOfTrailingZeros(nxt)
89+
nxt = nxt and (nxt - 1)
90+
if (((mask shr v) and 1) == 1) {
91+
continue
15492
}
155-
ans.add(tansList)
156-
return ans
157-
}
158-
val gm = findMinimalSolutions(wtc, tans, bg, ed)
159-
val minb = gm.minOf { Integer.bitCount(it) }
160-
val ns: MutableList<Int> = ArrayList<Int>()
161-
for (i in 0..25) {
162-
if (tans[i] == 1) {
163-
ns.add(i)
93+
if (color[v] == 1) {
94+
return true
16495
}
165-
}
166-
for (x in gm) {
167-
if (Integer.bitCount(x) == minb) {
168-
for (j in ns.indices) {
169-
if ((x and (1 shl j)) != 0) {
170-
tans[ns[j]] = 2
171-
} else {
172-
tans[ns[j]] = 1
173-
}
174-
}
175-
val tansList: MutableList<Int> = ArrayList<Int>()
176-
for (t in tans) {
177-
tansList.add(t)
178-
}
179-
ans.add(tansList)
96+
if (color[v] == 0 && dfs(v, color, mask)) {
97+
return true
18098
}
18199
}
182-
return ans
100+
color[u] = 2
101+
return false
183102
}
184103
}

0 commit comments

Comments
 (0)