Skip to content

Commit 9523620

Browse files
committed
Reorganise string
1 parent 1cbf0dc commit 9523620

File tree

2 files changed

+254
-0
lines changed

2 files changed

+254
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// 767. Reorganise string
2+
// Topics: 'Hash Table', 'String', 'Greedy', 'Sorting','Heap (Priority Queue)', 'Counting'
3+
// Level: 'Medium'
4+
5+
// Given a string s, rearrange the characters of s so that any two adjacent characters are not the same.
6+
7+
// Return any possible rearrangement of s or return "" if not possible.
8+
9+
// Example 1:
10+
11+
// Input: s = "aab"
12+
// Output: "aba"
13+
14+
// Example 2:
15+
16+
// Input: s = "aaab"
17+
// Output: ""
18+
19+
// Constraints:
20+
21+
// 1 <= s.length <= 500
22+
// s consists of lowercase English letters.
23+
24+
package reorganisestring
25+
26+
import (
27+
"container/heap"
28+
)
29+
30+
func reorganizeString(s string) string {
31+
m := map[byte]int{}
32+
for i := range s {
33+
m[s[i]]++
34+
}
35+
maxheap := maxheap{}
36+
for k, v := range m {
37+
heap.Push(&maxheap, symbol{times: v, b: k})
38+
}
39+
40+
var res []byte
41+
var prev *symbol
42+
for maxheap.Len() > 0 {
43+
pop := heap.Pop(&maxheap).(symbol)
44+
res = append(res, pop.b)
45+
pop.times--
46+
if prev != nil && prev.times > 0 {
47+
heap.Push(&maxheap, *prev)
48+
}
49+
if pop.times > 0 {
50+
prev = &pop
51+
} else {
52+
prev = nil
53+
}
54+
}
55+
if len(res) != len(s) {
56+
return ""
57+
}
58+
59+
return string(res)
60+
}
61+
62+
type symbol struct {
63+
times int
64+
b byte
65+
}
66+
67+
type maxheap []symbol
68+
69+
func (m maxheap) Len() int {
70+
return len(m)
71+
}
72+
73+
func (m maxheap) Swap(i, j int) {
74+
m[i], m[j] = m[j], m[i]
75+
}
76+
77+
func (m maxheap) Less(i, j int) bool {
78+
return m[i].times > m[j].times
79+
}
80+
81+
func (m *maxheap) Push(v interface{}) {
82+
*m = append(*m, v.(symbol))
83+
}
84+
85+
func (m *maxheap) Pop() interface{} {
86+
old := *m
87+
x := old[len(old)-1]
88+
*m = old[:len(old)-1]
89+
return x
90+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package reorganisestring
2+
3+
import "testing"
4+
5+
func TestReorganizeString(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
input string
9+
valid bool
10+
}{
11+
{
12+
name: "custom",
13+
input: "aaabc",
14+
valid: true,
15+
},
16+
{
17+
name: "example 1 - aab",
18+
input: "aab",
19+
valid: true,
20+
},
21+
{
22+
name: "example 2 - aaab",
23+
input: "aaab",
24+
valid: false,
25+
},
26+
{
27+
name: "single character",
28+
input: "a",
29+
valid: true,
30+
},
31+
{
32+
name: "two same characters",
33+
input: "aa",
34+
valid: false,
35+
},
36+
{
37+
name: "two different characters",
38+
input: "ab",
39+
valid: true,
40+
},
41+
{
42+
name: "all different characters",
43+
input: "abcde",
44+
valid: true,
45+
},
46+
{
47+
name: "valid rearrangement possible",
48+
input: "aaabbc",
49+
valid: true,
50+
},
51+
{
52+
name: "longer valid string",
53+
input: "aabbcc",
54+
valid: true,
55+
},
56+
{
57+
name: "edge case - barely possible",
58+
input: "aaab",
59+
valid: false,
60+
},
61+
{
62+
name: "edge case - just possible",
63+
input: "aabb",
64+
valid: true,
65+
},
66+
{
67+
name: "multiple different frequencies",
68+
input: "aaabbbbccd",
69+
valid: true,
70+
},
71+
{
72+
name: "long string with valid distribution",
73+
input: "aaaaabbbbbccccc",
74+
valid: true,
75+
},
76+
{
77+
name: "impossible case - one char dominates",
78+
input: "aaaaabbc",
79+
valid: false,
80+
},
81+
}
82+
83+
for _, tt := range tests {
84+
t.Run(tt.name, func(t *testing.T) {
85+
result := reorganizeString(tt.input)
86+
87+
if tt.valid {
88+
if result == "" {
89+
t.Errorf("Expected valid rearrangement but got empty string")
90+
return
91+
}
92+
93+
if len(result) != len(tt.input) {
94+
t.Errorf("Result length %d doesn't match input length %d", len(result), len(tt.input))
95+
return
96+
}
97+
98+
charCount := make(map[byte]int)
99+
for i := range tt.input {
100+
charCount[tt.input[i]]++
101+
}
102+
for i := range result {
103+
charCount[result[i]]--
104+
}
105+
for char, count := range charCount {
106+
if count != 0 {
107+
t.Errorf("Character %c count mismatch: %d", char, count)
108+
}
109+
}
110+
111+
for i := 0; i < len(result)-1; i++ {
112+
if result[i] == result[i+1] {
113+
t.Errorf("Found adjacent same characters at position %d: %c", i, result[i])
114+
}
115+
}
116+
} else {
117+
if result != "" {
118+
t.Errorf("Expected empty string but got: %s", result)
119+
}
120+
}
121+
})
122+
}
123+
}
124+
125+
func TestReorganizeStringCharacterFrequency(t *testing.T) {
126+
input := "aabbcc"
127+
result := reorganizeString(input)
128+
129+
if result == "" {
130+
t.Fatal("Expected non-empty result")
131+
}
132+
133+
freq := make(map[byte]int)
134+
for i := range input {
135+
freq[input[i]]++
136+
}
137+
138+
for i := range result {
139+
freq[result[i]]--
140+
}
141+
142+
for char, count := range freq {
143+
if count != 0 {
144+
t.Errorf("Character frequency mismatch for %c: %d", char, count)
145+
}
146+
}
147+
}
148+
149+
func TestReorganizeStringNoAdjacentDuplicates(t *testing.T) {
150+
testCases := []string{"aab", "aabbcc", "aaabbbbccccc"}
151+
152+
for _, input := range testCases {
153+
result := reorganizeString(input)
154+
if result == "" {
155+
continue
156+
}
157+
158+
for i := 0; i < len(result)-1; i++ {
159+
if result[i] == result[i+1] {
160+
t.Errorf("Input %s: Found adjacent duplicates in result %s at position %d", input, result, i)
161+
}
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)