|  | 
| 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