|
1 | 1 | package g3401_3500.s3435_frequencies_of_shortest_supersequences; |
2 | 2 |
|
3 | 3 | // #Hard #Array #String #Bit_Manipulation #Graph #Enumeration #Topological_Sort |
4 | | -// #2025_01_29_Time_16_ms_(95.35%)_Space_45.52_MB_(93.02%) |
| 4 | +// #2025_04_04_Time_20_ms_(97.26%)_Space_45.52_MB_(83.56%) |
5 | 5 |
|
6 | 6 | import java.util.ArrayList; |
7 | | -import java.util.Arrays; |
8 | | -import java.util.HashSet; |
9 | 7 | import java.util.List; |
10 | | -import java.util.Set; |
11 | 8 |
|
| 9 | +@SuppressWarnings("unchecked") |
12 | 10 | public class Solution { |
13 | | - private int m; |
14 | | - private int forcedMask; |
15 | | - private int[] adj; |
16 | | - private char[] idxToChar = new char[26]; |
17 | | - private int[] charToIdx = new int[26]; |
18 | | - private boolean[] used = new boolean[26]; |
| 11 | + private int min = Integer.MAX_VALUE; |
| 12 | + private List<int[]> lists = new ArrayList<>(); |
19 | 13 |
|
20 | 14 | public List<List<Integer>> supersequences(String[] words) { |
21 | | - Arrays.fill(charToIdx, -1); |
22 | | - for (String w : words) { |
23 | | - used[w.charAt(0) - 'a'] = true; |
24 | | - used[w.charAt(1) - 'a'] = true; |
| 15 | + boolean[][] pairs = new boolean[26][26]; |
| 16 | + int[] counts = new int[26]; |
| 17 | + for (String word : words) { |
| 18 | + int a = word.charAt(0) - 'a'; |
| 19 | + int b = word.charAt(1) - 'a'; |
| 20 | + if (!pairs[a][b]) { |
| 21 | + pairs[a][b] = true; |
| 22 | + counts[a]++; |
| 23 | + counts[b]++; |
| 24 | + } |
| 25 | + } |
| 26 | + List<Integer>[] links = new ArrayList[26]; |
| 27 | + for (int i = 0; i < 26; i++) { |
| 28 | + links[i] = new ArrayList<>(); |
25 | 29 | } |
26 | | - // Map each used letter to an index [0..m-1] |
27 | | - for (int c = 0; c < 26; c++) { |
28 | | - if (used[c]) { |
29 | | - idxToChar[m] = (char) (c + 'a'); |
30 | | - charToIdx[c] = m++; |
| 30 | + int[] counts1 = new int[26]; |
| 31 | + int[] sides = new int[26]; |
| 32 | + for (int i = 0; i < 26; i++) { |
| 33 | + for (int j = 0; j < 26; j++) { |
| 34 | + if (pairs[i][j]) { |
| 35 | + links[i].add(j); |
| 36 | + counts1[j]++; |
| 37 | + sides[i] |= 1; |
| 38 | + sides[j] |= 2; |
| 39 | + } |
31 | 40 | } |
32 | 41 | } |
33 | | - adj = new int[m]; |
34 | | - // Build graph and record forced duplicates |
35 | | - for (String w : words) { |
36 | | - int u = charToIdx[w.charAt(0) - 'a']; |
37 | | - int v = charToIdx[w.charAt(1) - 'a']; |
38 | | - if (u == v) { |
39 | | - forcedMask |= (1 << u); |
| 42 | + int[] arr = new int[26]; |
| 43 | + for (int i = 0; i < 26; i++) { |
| 44 | + if (counts[i] <= 1) { |
| 45 | + arr[i] = counts[i]; |
| 46 | + } else if (counts1[i] == 0 || sides[i] != 3) { |
| 47 | + arr[i] = 1; |
| 48 | + } else if (pairs[i][i]) { |
| 49 | + arr[i] = 2; |
40 | 50 | } else { |
41 | | - adj[u] |= (1 << v); |
| 51 | + arr[i] = -1; |
42 | 52 | } |
43 | 53 | } |
44 | | - // Try all supersets of forcedMask; keep those that kill all cycles |
45 | | - int best = 9999; |
46 | | - List<Integer> goodSets = new ArrayList<>(); |
47 | | - for (int s = 0; s < (1 << m); s++) { |
48 | | - if ((s & forcedMask) != forcedMask) { |
49 | | - continue; |
50 | | - } |
51 | | - int size = Integer.bitCount(s); |
52 | | - if (size <= best && !hasCycle(s)) { |
53 | | - if (size < best) { |
54 | | - best = size; |
55 | | - goodSets.clear(); |
56 | | - } |
57 | | - goodSets.add(s); |
| 54 | + dfs(links, 0, arr, new int[26], 0); |
| 55 | + List<List<Integer>> res = new ArrayList<>(); |
| 56 | + for (int[] arr1 : lists) { |
| 57 | + List<Integer> list = new ArrayList<>(); |
| 58 | + for (int n : arr1) { |
| 59 | + list.add(n); |
58 | 60 | } |
| 61 | + res.add(list); |
| 62 | + } |
| 63 | + return res; |
| 64 | + } |
| 65 | + |
| 66 | + private void dfs(List<Integer>[] links, int i, int[] arr1, int[] arr, int n) { |
| 67 | + if (n > min) { |
| 68 | + return; |
59 | 69 | } |
60 | | - // Build distinct freq arrays from these sets |
61 | | - Set<String> seen = new HashSet<>(); |
62 | | - List<List<Integer>> ans = new ArrayList<>(); |
63 | | - for (int s : goodSets) { |
64 | | - int[] freq = new int[26]; |
65 | | - for (int i = 0; i < m; i++) { |
66 | | - freq[idxToChar[i] - 'a'] = ((s & (1 << i)) != 0) ? 2 : 1; |
| 70 | + if (i == 26) { |
| 71 | + if (!chk(links, arr)) { |
| 72 | + return; |
67 | 73 | } |
68 | | - String key = Arrays.toString(freq); |
69 | | - if (seen.add(key)) { |
70 | | - List<Integer> tmp = new ArrayList<>(); |
71 | | - for (int f : freq) { |
72 | | - tmp.add(f); |
73 | | - } |
74 | | - ans.add(tmp); |
| 74 | + if (n < min) { |
| 75 | + min = n; |
| 76 | + lists = new ArrayList<>(); |
| 77 | + lists.add(arr.clone()); |
| 78 | + } else if (n == min) { |
| 79 | + lists.add(arr.clone()); |
75 | 80 | } |
| 81 | + return; |
| 82 | + } |
| 83 | + if (arr1[i] >= 0) { |
| 84 | + arr[i] = arr1[i]; |
| 85 | + dfs(links, i + 1, arr1, arr, n + arr1[i]); |
| 86 | + } else { |
| 87 | + arr[i] = 1; |
| 88 | + dfs(links, i + 1, arr1, arr, n + 1); |
| 89 | + arr[i] = 2; |
| 90 | + dfs(links, i + 1, arr1, arr, n + 2); |
76 | 91 | } |
77 | | - return ans; |
78 | 92 | } |
79 | 93 |
|
80 | | - private boolean hasCycle(int mask) { |
81 | | - int[] color = new int[m]; |
82 | | - for (int i = 0; i < m; i++) { |
83 | | - if (((mask >> i) & 1) == 0 && color[i] == 0 && dfs(i, color, mask)) { |
84 | | - return true; |
| 94 | + private boolean chk(List<Integer>[] links, int[] arr) { |
| 95 | + for (int i = 0; i < 26; i++) { |
| 96 | + if (arr[i] == 1 && dfs1(links, arr, new boolean[26], i)) { |
| 97 | + return false; |
85 | 98 | } |
86 | 99 | } |
87 | | - return false; |
| 100 | + return true; |
88 | 101 | } |
89 | 102 |
|
90 | | - private boolean dfs(int u, int[] color, int mask) { |
91 | | - color[u] = 1; |
92 | | - int nxt = adj[u]; |
93 | | - while (nxt != 0) { |
94 | | - int v = Integer.numberOfTrailingZeros(nxt); |
95 | | - nxt &= (nxt - 1); |
96 | | - if (((mask >> v) & 1) == 1) { |
97 | | - continue; |
98 | | - } |
99 | | - if (color[v] == 1) { |
100 | | - return true; |
101 | | - } |
102 | | - if (color[v] == 0 && dfs(v, color, mask)) { |
| 103 | + private boolean dfs1(List<Integer>[] links, int[] arr, boolean[] seens, int i) { |
| 104 | + seens[i] = true; |
| 105 | + for (int next : links[i]) { |
| 106 | + if (arr[next] == 1 && (seens[next] || dfs1(links, arr, seens, next))) { |
103 | 107 | return true; |
104 | 108 | } |
105 | 109 | } |
106 | | - color[u] = 2; |
| 110 | + seens[i] = false; |
107 | 111 | return false; |
108 | 112 | } |
109 | 113 | } |
0 commit comments