Custom Sort String
Reorder `s` so its characters follow the custom alphabet `order` first.
3 min read
hash-map sorting strings
Problem#
Given strings order and s, return any permutation of s such that characters appearing in order come first, in the same relative order as order. Characters not in order may appear in any position.
Examples#
order = "cba", s = "abcd"→"cbad"(or"dcba", etc.).order = "bcafg", s = "abcd"→"bcad".
Constraints#
1 <= order.length <= 26,1 <= s.length <= 200. Both contain only lowercase letters.
Hints#
Hint
Count letters in
s. Emit order letters first (decrementing counts), then the remaining letters in any order. Approach#
Counter array cnt[26] over s. For each char in order (in order), append it cnt[c] times and zero its count. Then append any remaining letters with non-zero counts (their order is unconstrained).
Solution#
class Solution {public: string customSortString(string order, string s) { int cnt[26] = {0}; for (char c : s) ++cnt[c - 'a']; string ans; ans.reserve(s.size()); for (char c : order) { int idx = c - 'a'; ans.append(cnt[idx], c); cnt[idx] = 0; } for (int i = 0; i < 26; ++i) ans.append(cnt[i], 'a' + i); return ans; }};func customSortString(order string, s string) string { var cnt [26]int for _, c := range s { cnt[c-'a']++ } ans := make([]byte, 0, len(s)) for i := 0; i < len(order); i++ { c := order[i] idx := c - 'a' for k := 0; k < cnt[idx]; k++ { ans = append(ans, c) } cnt[idx] = 0 } for i := 0; i < 26; i++ { for k := 0; k < cnt[i]; k++ { ans = append(ans, byte('a'+i)) } } return string(ans)}from collections import Counter
class Solution: def customSortString(self, order: str, s: str) -> str: cnt = Counter(s) parts = [] for c in order: if cnt[c]: parts.append(c * cnt[c]) cnt[c] = 0 for c, k in cnt.items(): if k: parts.append(c * k) return ''.join(parts)function customSortString(order, s) { const cnt = new Array(26).fill(0); const A = 'a'.charCodeAt(0); for (const c of s) cnt[c.charCodeAt(0) - A]++; const parts = []; for (const c of order) { const idx = c.charCodeAt(0) - A; if (cnt[idx]) { parts.push(c.repeat(cnt[idx])); cnt[idx] = 0; } } for (let i = 0; i < 26; i++) { if (cnt[i]) parts.push(String.fromCharCode(A + i).repeat(cnt[i])); } return parts.join('');}class Solution { public String customSortString(String order, String s) { int[] cnt = new int[26]; for (int i = 0; i < s.length(); i++) cnt[s.charAt(i) - 'a']++; StringBuilder ans = new StringBuilder(s.length()); for (int i = 0; i < order.length(); i++) { char c = order.charAt(i); int idx = c - 'a'; for (int k = 0; k < cnt[idx]; k++) ans.append(c); cnt[idx] = 0; } for (int i = 0; i < 26; i++) { for (int k = 0; k < cnt[i]; k++) ans.append((char) ('a' + i)); } return ans.toString(); }}function customSortString(order: string, s: string): string { const cnt: number[] = new Array(26).fill(0); const A = 'a'.charCodeAt(0); for (const c of s) cnt[c.charCodeAt(0) - A]++; const parts: string[] = []; for (const c of order) { const idx = c.charCodeAt(0) - A; if (cnt[idx]) { parts.push(c.repeat(cnt[idx])); cnt[idx] = 0; } } for (let i = 0; i < 26; i++) { if (cnt[i]) parts.push(String.fromCharCode(A + i).repeat(cnt[i])); } return parts.join('');}Editorial#
Linear emission beats comparator-based sorting (O(N log N)) for short alphabets — we know the rank of each letter from order’s position. The two-phase emit (ordered then leftover) handles letters outside order cleanly.
Complexity#
- Time: O(N + 26).
- Space: O(1) extra.
Concept revision#
Related#