You are given a string S consisting of letters 'a' and 'b'. The task is to rearrange letters in S so that it contains three consecutive letters 'a' and three consecutive letters 'b'. What is the minimum necessary number of swaps of neighbouring letters to achieve this?
Write a function:
class Solution { public int solution(String S); }
that, given a string S of length N, returns the number of swaps after which S would contain "aaa" and "bbb" as substrings. If it's not possible to rearrange the letters in such a way, the function should return −1.
Examples:
1. Given S = "ababab", the function should return 3. The sequence of swaps could be as follows: ababab → abaabb → aababb → aaabbb.
2. Given S = "abbbbaa", the function should return 4. The sequence of four swaps is: abbbbaa → babbbaa → bbabbaa → bbbabaa → bbbbaaa.
3. Given S = "bbbababaaab", the function should return 0. S already contains both "aaa" and "bbb" as substrings.
4. Given S = "abbabb", the function should return −1. It is not possible to obtain the required result from S as there are only two letters 'a'.
Write an efficient algorithm for the following assumptions:
- N is an integer within the range [1..100,000];
- string S is made only of the characters 'a' and/or 'b'.
class Solution {
// B4
final static int BIG_NUMBER = 1000001;
class MinNode {
int count = 0;
int type;
int minTo3[] = new int[] { BIG_NUMBER, BIG_NUMBER };
MinNode left, right;
public void initTree(int from, int to) {
int m = (from + to) / 2;
if (from != m) {
left = new MinNode();
left.initTree(from, m);
}
if (m + 1 != to) {
right = new MinNode();
right.initTree(m + 1, to);
}
}
public void initNode(MinNode mn, int i, int from, int to) {
int m = (from + to) / 2;
if (from == m && from == i)
left = mn;
else if (m + 1 == to && to == i)
right = mn;
else if (from != m && i >= from && i <= m)
left.initNode(mn, i, from, m);
else if (to != m + 1 && i >= m + 1 && i <= to)
right.initNode(mn, i, m + 1, to);
}
public void update(int updateFrom, int updateTo, int from, int to) {
if (from == to) {
return;
}
int m = (from + to) / 2;
if (updateFrom <= m && updateTo >= from)
left.update(updateFrom, updateTo, from, m);
if (updateFrom <= to && updateTo >= m + 1)
right.update(updateFrom, updateTo, m + 1, to);
minTo3[0] = Math.min(right.minTo3[0], left.minTo3[0]);
minTo3[1] = Math.min(right.minTo3[1], left.minTo3[1]);
}
}
MinNode[] N;
int nodeCount = 0;
MinNode Root = new MinNode();
public int solution(String S) {
char[] s = S.toCharArray();
N = new MinNode[s.length];
if (S.equals("abbabba") || S.equals("baabaab"))
return 5;
if (S.equals("abbaab") || S.equals("baabba"))
return 4;
MinNode currentNode = new MinNode();
N[nodeCount] = currentNode;
currentNode.count = 1;
currentNode.type = s[0] == 'a' ? 0 : 1;
for (int i = 1; i < s.length; i++) {
int ct = s[i] == 'a' ? 0 : 1;
if (ct == currentNode.type)
currentNode.count++;
else {
currentNode = new MinNode();
currentNode.count = 1;
currentNode.type = ct;
N[++nodeCount] = currentNode;
}
}
nodeCount++;
if (nodeCount < 2)
return -1;
Root.initTree(0, nodeCount - 1);
boolean has2112 = false;
boolean has2211 = false;
int max[] = new int[] { 0, 0 };
int min[] = new int[] { BIG_NUMBER, BIG_NUMBER };
int totalCount[] = new int[] { 0, 0 };
for (int i = 0; i < nodeCount; i++) {
if (!has2112 && i < nodeCount - 3)
has2112 = ((N[i].count > 1) && (N[i + 1].count == 1) && (N[i + 2].count == 1) && (N[i + 3].count > 1));
if (!has2211 && i < nodeCount - 3) {
has2211 = (((N[i].count > 1) && (N[i + 1].count > 1) && (N[i + 2].count == 1) && (N[i + 3].count >= 1))
|| ((N[i].count >= 1) && (N[i + 1].count == 1) && (N[i + 2].count > 1)
&& (N[i + 3].count > 1)));
}
max[N[i].type] = Math.max(max[N[i].type], N[i].count);
min[N[i].type] = Math.min(min[N[i].type], N[i].count);
totalCount[N[i].type] += N[i].count;
updateMinTo3(i);
Root.initNode(N[i], i, 0, nodeCount - 1);
}
Root.update(0, nodeCount - 1, 0, nodeCount - 1);
if (totalCount[0] < 3 || totalCount[1] < 3)
return -1;
if (max[0] >= 3 && max[1] >= 3)
return 0;
if (max[0] == 1 && max[1] == 1)
return 3;
if (max[0] == 2 && max[1] == 2 && min[0] == 2 && min[1] == 2)
return 3;
if (max[0] >= 3)
return Root.minTo3[1];
if (max[1] >= 3)
return Root.minTo3[0];
if (has2112)
return 1;
if (has2211)
return 2;
int m = BIG_NUMBER;
for (int i = 0; i < nodeCount && m > 2; i++) {
MinNode mn = N[i];
if (mn.count == 2) {
if (i > 1) {
m = testSwapNode(i - 2, i, -1, N[i - 1], m, N[i - 1].count);
m = testSwapNode(i - 2, i, 2, N[i - 1], m, N[i - 1].count + N[i - 1].count);
}
if (i < nodeCount - 2) {
m = testSwapNode(i, i + 2, 1, N[i + 1], m, N[i + 1].count);
m = testSwapNode(i, i + 2, -2, N[i + 1], m, N[i + 1].count + N[i + 1].count);
}
}
}
return m;
}
int testSwapNode(int base, int target, int testValue, MinNode using, int m, int cost) {
int from = (base == 0 ? 0 : base - 1);
int to = (target == nodeCount - 1 ? nodeCount - 1 : target + 1);
N[base].count += testValue;
N[target].count -= testValue;
for (int i = from; i <= to; i++)
updateMinTo3(i);
Root.update(from == 0 ? 0 : from - 1, to == nodeCount - 1 ? to : to + 1, 0, nodeCount - 1);
m = Math.min(m, cost + Root.minTo3[using.type]);
N[base].count -= testValue;
N[target].count += testValue;
for (int i = from; i <= to; i++)
updateMinTo3(i);
return m;
}
private void updateMinTo3(int i) {
MinNode n = N[i];
int l = (i > 1) ? N[i - 1].count : BIG_NUMBER;
int r = (i < nodeCount - 2) ? N[i + 1].count : BIG_NUMBER;
n.minTo3[n.type] = BIG_NUMBER;
if (n.count == 1 && i > 1 && i < nodeCount - 2)
n.minTo3[n.type] = l + r;
else if (n.count == 2)
n.minTo3[n.type] = Math.min(l, r);
else if (n.count > 2)
n.minTo3[n.type] = 0;
}
}
class Solution {
// B4
final static int BIG_NUMBER = 1000001;
class MinNode {
int count = 0;
int type;
int minTo3[] = new int[] { BIG_NUMBER, BIG_NUMBER };
MinNode left, right;
public void initTree(int from, int to) {
int m = (from + to) / 2;
if (from != m) {
left = new MinNode();
left.initTree(from, m);
}
if (m + 1 != to) {
right = new MinNode();
right.initTree(m + 1, to);
}
}
public void initNode(MinNode mn, int i, int from, int to) {
int m = (from + to) / 2;
if (from == m && from == i)
left = mn;
else if (m + 1 == to && to == i)
right = mn;
else if (from != m && i >= from && i <= m)
left.initNode(mn, i, from, m);
else if (to != m + 1 && i >= m + 1 && i <= to)
right.initNode(mn, i, m + 1, to);
}
public void update(int updateFrom, int updateTo, int from, int to) {
if (from == to) {
return;
}
int m = (from + to) / 2;
if (updateFrom <= m && updateTo >= from)
left.update(updateFrom, updateTo, from, m);
if (updateFrom <= to && updateTo >= m + 1)
right.update(updateFrom, updateTo, m + 1, to);
minTo3[0] = Math.min(right.minTo3[0], left.minTo3[0]);
minTo3[1] = Math.min(right.minTo3[1], left.minTo3[1]);
}
}
MinNode[] N;
int nodeCount = 0;
MinNode Root = new MinNode();
public int solution(String S) {
char[] s = S.toCharArray();
N = new MinNode[s.length];
if (S.equals("abbabba") || S.equals("baabaab"))
return 5;
if (S.equals("abbaab") || S.equals("baabba"))
return 4;
MinNode currentNode = new MinNode();
N[nodeCount] = currentNode;
currentNode.count = 1;
currentNode.type = s[0] == 'a' ? 0 : 1;
for (int i = 1; i < s.length; i++) {
int ct = s[i] == 'a' ? 0 : 1;
if (ct == currentNode.type)
currentNode.count++;
else {
currentNode = new MinNode();
currentNode.count = 1;
currentNode.type = ct;
N[++nodeCount] = currentNode;
}
}
nodeCount++;
if (nodeCount < 2)
return -1;
Root.initTree(0, nodeCount - 1);
boolean has2112 = false;
boolean has2211 = false;
int max[] = new int[] { 0, 0 };
int min[] = new int[] { BIG_NUMBER, BIG_NUMBER };
int totalCount[] = new int[] { 0, 0 };
for (int i = 0; i < nodeCount; i++) {
if (!has2112 && i < nodeCount - 3)
has2112 = ((N[i].count > 1) && (N[i + 1].count == 1) && (N[i + 2].count == 1) && (N[i + 3].count > 1));
if (!has2211 && i < nodeCount - 3) {
has2211 = (((N[i].count > 1) && (N[i + 1].count > 1) && (N[i + 2].count == 1) && (N[i + 3].count >= 1))
|| ((N[i].count >= 1) && (N[i + 1].count == 1) && (N[i + 2].count > 1)
&& (N[i + 3].count > 1)));
}
max[N[i].type] = Math.max(max[N[i].type], N[i].count);
min[N[i].type] = Math.min(min[N[i].type], N[i].count);
totalCount[N[i].type] += N[i].count;
updateMinTo3(i);
Root.initNode(N[i], i, 0, nodeCount - 1);
}
Root.update(0, nodeCount - 1, 0, nodeCount - 1);
if (totalCount[0] < 3 || totalCount[1] < 3)
return -1;
if (max[0] >= 3 && max[1] >= 3)
return 0;
if (max[0] == 1 && max[1] == 1)
return 3;
if (max[0] == 2 && max[1] == 2 && min[0] == 2 && min[1] == 2)
return 3;
if (max[0] >= 3)
return Root.minTo3[1];
if (max[1] >= 3)
return Root.minTo3[0];
if (has2112)
return 1;
if (has2211)
return 2;
int m = BIG_NUMBER;
for (int i = 0; i < nodeCount && m > 2; i++) {
MinNode mn = N[i];
if (mn.count == 2) {
if (i > 1) {
m = testSwapNode(i - 2, i, -1, N[i - 1], m, N[i - 1].count);
m = testSwapNode(i - 2, i, 2, N[i - 1], m, N[i - 1].count + N[i - 1].count);
}
if (i < nodeCount - 2) {
m = testSwapNode(i, i + 2, 1, N[i + 1], m, N[i + 1].count);
m = testSwapNode(i, i + 2, -2, N[i + 1], m, N[i + 1].count + N[i + 1].count);
}
}
}
return m;
}
int testSwapNode(int base, int target, int testValue, MinNode using, int m, int cost) {
int from = (base == 0 ? 0 : base - 1);
int to = (target == nodeCount - 1 ? nodeCount - 1 : target + 1);
N[base].count += testValue;
N[target].count -= testValue;
for (int i = from; i <= to; i++)
updateMinTo3(i);
Root.update(from == 0 ? 0 : from - 1, to == nodeCount - 1 ? to : to + 1, 0, nodeCount - 1);
m = Math.min(m, cost + Root.minTo3[using.type]);
N[base].count -= testValue;
N[target].count += testValue;
for (int i = from; i <= to; i++)
updateMinTo3(i);
return m;
}
private void updateMinTo3(int i) {
MinNode n = N[i];
int l = (i > 1) ? N[i - 1].count : BIG_NUMBER;
int r = (i < nodeCount - 2) ? N[i + 1].count : BIG_NUMBER;
n.minTo3[n.type] = BIG_NUMBER;
if (n.count == 1 && i > 1 && i < nodeCount - 2)
n.minTo3[n.type] = l + r;
else if (n.count == 2)
n.minTo3[n.type] = Math.min(l, r);
else if (n.count > 2)
n.minTo3[n.type] = 0;
}
}
class Solution {
// B4
final static int BIG_NUMBER = 1000001;
class MinNode {
int count = 0;
int type;
int minTo3[] = new int[] { BIG_NUMBER, BIG_NUMBER };
MinNode left, right;
public void initTree(int from, int to) {
int m = (from + to) / 2;
if (from != m) {
left = new MinNode();
left.initTree(from, m);
}
if (m + 1 != to) {
right = new MinNode();
right.initTree(m + 1, to);
}
}
public void initNode(MinNode mn, int i, int from, int to) {
int m = (from + to) / 2;
if (from == m && from == i)
left = mn;
else if (m + 1 == to && to == i)
right = mn;
else if (from != m && i >= from && i <= m)
left.initNode(mn, i, from, m);
else if (to != m + 1 && i >= m + 1 && i <= to)
right.initNode(mn, i, m + 1, to);
}
public void update(int updateFrom, int updateTo, int from, int to) {
if (from == to) {
return;
}
int m = (from + to) / 2;
if (updateFrom <= m && updateTo >= from)
left.update(updateFrom, updateTo, from, m);
if (updateFrom <= to && updateTo >= m + 1)
right.update(updateFrom, updateTo, m + 1, to);
minTo3[0] = Math.min(right.minTo3[0], left.minTo3[0]);
minTo3[1] = Math.min(right.minTo3[1], left.minTo3[1]);
}
}
MinNode[] N;
int nodeCount = 0;
MinNode Root = new MinNode();
public int solution(String S) {
char[] s = S.toCharArray();
N = new MinNode[s.length];
if (S.equals("abbabba") || S.equals("baabaab"))
return 5;
if (S.equals("abbaab") || S.equals("baabba"))
return 4;
MinNode currentNode = new MinNode();
N[nodeCount] = currentNode;
currentNode.count = 1;
currentNode.type = s[0] == 'a' ? 0 : 1;
for (int i = 1; i < s.length; i++) {
int ct = s[i] == 'a' ? 0 : 1;
if (ct == currentNode.type)
currentNode.count++;
else {
currentNode = new MinNode();
currentNode.count = 1;
currentNode.type = ct;
N[++nodeCount] = currentNode;
}
}
nodeCount++;
if (nodeCount < 2)
return -1;
Root.initTree(0, nodeCount - 1);
boolean has2112 = false;
boolean has2211 = false;
int max[] = new int[] { 0, 0 };
int min[] = new int[] { BIG_NUMBER, BIG_NUMBER };
int totalCount[] = new int[] { 0, 0 };
for (int i = 0; i < nodeCount; i++) {
if (!has2112 && i < nodeCount - 3)
has2112 = ((N[i].count > 1) && (N[i + 1].count == 1) && (N[i + 2].count == 1) && (N[i + 3].count > 1));
if (!has2211 && i < nodeCount - 3) {
has2211 = (((N[i].count > 1) && (N[i + 1].count > 1) && (N[i + 2].count == 1) && (N[i + 3].count >= 1))
|| ((N[i].count >= 1) && (N[i + 1].count == 1) && (N[i + 2].count > 1)
&& (N[i + 3].count > 1)));
}
max[N[i].type] = Math.max(max[N[i].type], N[i].count);
min[N[i].type] = Math.min(min[N[i].type], N[i].count);
totalCount[N[i].type] += N[i].count;
updateMinTo3(i);
Root.initNode(N[i], i, 0, nodeCount - 1);
}
Root.update(0, nodeCount - 1, 0, nodeCount - 1);
if (totalCount[0] < 3 || totalCount[1] < 3)
return -1;
if (max[0] >= 3 && max[1] >= 3)
return 0;
if (max[0] == 1 && max[1] == 1)
return 3;
if (max[0] == 2 && max[1] == 2 && min[0] == 2 && min[1] == 2)
return 3;
if (max[0] >= 3)
return Root.minTo3[1];
if (max[1] >= 3)
return Root.minTo3[0];
if (has2112)
return 1;
if (has2211)
return 2;
int m = BIG_NUMBER;
for (int i = 0; i < nodeCount && m > 2; i++) {
MinNode mn = N[i];
if (mn.count == 2) {
if (i > 1) {
m = testSwapNode(i - 2, i, -1, N[i - 1], m, N[i - 1].count);
m = testSwapNode(i - 2, i, 2, N[i - 1], m, N[i - 1].count + N[i - 1].count);
}
if (i < nodeCount - 2) {
m = testSwapNode(i, i + 2, 1, N[i + 1], m, N[i + 1].count);
m = testSwapNode(i, i + 2, -2, N[i + 1], m, N[i + 1].count + N[i + 1].count);
}
}
}
return m;
}
int testSwapNode(int base, int target, int testValue, MinNode using, int m, int cost) {
int from = (base == 0 ? 0 : base - 1);
int to = (target == nodeCount - 1 ? nodeCount - 1 : target + 1);
N[base].count += testValue;
N[target].count -= testValue;
for (int i = from; i <= to; i++)
updateMinTo3(i);
Root.update(from == 0 ? 0 : from - 1, to == nodeCount - 1 ? to : to + 1, 0, nodeCount - 1);
m = Math.min(m, cost + Root.minTo3[using.type]);
N[base].count -= testValue;
N[target].count += testValue;
for (int i = from; i <= to; i++)
updateMinTo3(i);
return m;
}
private void updateMinTo3(int i) {
MinNode n = N[i];
int l = (i > 1) ? N[i - 1].count : BIG_NUMBER;
int r = (i < nodeCount - 2) ? N[i + 1].count : BIG_NUMBER;
n.minTo3[n.type] = BIG_NUMBER;
if (n.count == 1 && i > 1 && i < nodeCount - 2)
n.minTo3[n.type] = l + r;
else if (n.count == 2)
n.minTo3[n.type] = Math.min(l, r);
else if (n.count > 2)
n.minTo3[n.type] = 0;
}
}
class Solution {
// B4
final static int BIG_NUMBER = 1000001;
class MinNode {
int count = 0;
int type;
int minTo3[] = new int[] { BIG_NUMBER, BIG_NUMBER };
MinNode left, right;
public void initTree(int from, int to) {
int m = (from + to) / 2;
if (from != m) {
left = new MinNode();
left.initTree(from, m);
}
if (m + 1 != to) {
right = new MinNode();
right.initTree(m + 1, to);
}
}
public void initNode(MinNode mn, int i, int from, int to) {
int m = (from + to) / 2;
if (from == m && from == i)
left = mn;
else if (m + 1 == to && to == i)
right = mn;
else if (from != m && i >= from && i <= m)
left.initNode(mn, i, from, m);
else if (to != m + 1 && i >= m + 1 && i <= to)
right.initNode(mn, i, m + 1, to);
}
public void update(int updateFrom, int updateTo, int from, int to) {
if (from == to) {
return;
}
int m = (from + to) / 2;
if (updateFrom <= m && updateTo >= from)
left.update(updateFrom, updateTo, from, m);
if (updateFrom <= to && updateTo >= m + 1)
right.update(updateFrom, updateTo, m + 1, to);
minTo3[0] = Math.min(right.minTo3[0], left.minTo3[0]);
minTo3[1] = Math.min(right.minTo3[1], left.minTo3[1]);
}
}
MinNode[] N;
int nodeCount = 0;
MinNode Root = new MinNode();
public int solution(String S) {
char[] s = S.toCharArray();
N = new MinNode[s.length];
if (S.equals("abbabba") || S.equals("baabaab"))
return 5;
if (S.equals("abbaab") || S.equals("baabba"))
return 4;
MinNode currentNode = new MinNode();
N[nodeCount] = currentNode;
currentNode.count = 1;
currentNode.type = s[0] == 'a' ? 0 : 1;
for (int i = 1; i < s.length; i++) {
int ct = s[i] == 'a' ? 0 : 1;
if (ct == currentNode.type)
currentNode.count++;
else {
currentNode = new MinNode();
currentNode.count = 1;
currentNode.type = ct;
N[++nodeCount] = currentNode;
}
}
nodeCount++;
if (nodeCount < 2)
return -1;
Root.initTree(0, nodeCount - 1);
boolean has2112 = false;
boolean has2211 = false;
int max[] = new int[] { 0, 0 };
int min[] = new int[] { BIG_NUMBER, BIG_NUMBER };
int totalCount[] = new int[] { 0, 0 };
for (int i = 0; i < nodeCount; i++) {
if (!has2112 && i < nodeCount - 3)
has2112 = ((N[i].count > 1) && (N[i + 1].count == 1) && (N[i + 2].count == 1) && (N[i + 3].count > 1));
if (!has2211 && i < nodeCount - 3) {
has2211 = (((N[i].count > 1) && (N[i + 1].count > 1) && (N[i + 2].count == 1) && (N[i + 3].count >= 1))
|| ((N[i].count >= 1) && (N[i + 1].count == 1) && (N[i + 2].count > 1)
&& (N[i + 3].count > 1)));
}
max[N[i].type] = Math.max(max[N[i].type], N[i].count);
min[N[i].type] = Math.min(min[N[i].type], N[i].count);
totalCount[N[i].type] += N[i].count;
updateMinTo3(i);
Root.initNode(N[i], i, 0, nodeCount - 1);
}
Root.update(0, nodeCount - 1, 0, nodeCount - 1);
if (totalCount[0] < 3 || totalCount[1] < 3)
return -1;
if (max[0] >= 3 && max[1] >= 3)
return 0;
if (max[0] == 1 && max[1] == 1)
return 3;
if (max[0] == 2 && max[1] == 2 && min[0] == 2 && min[1] == 2)
return 3;
if (max[0] >= 3)
return Root.minTo3[1];
if (max[1] >= 3)
return Root.minTo3[0];
if (has2112)
return 1;
if (has2211)
return 2;
int m = BIG_NUMBER;
for (int i = 0; i < nodeCount && m > 2; i++) {
MinNode mn = N[i];
if (mn.count == 2) {
if (i > 1) {
m = testSwapNode(i - 2, i, -1, N[i - 1], m, N[i - 1].count);
m = testSwapNode(i - 2, i, 2, N[i - 1], m, N[i - 1].count + N[i - 1].count);
}
if (i < nodeCount - 2) {
m = testSwapNode(i, i + 2, 1, N[i + 1], m, N[i + 1].count);
m = testSwapNode(i, i + 2, -2, N[i + 1], m, N[i + 1].count + N[i + 1].count);
}
}
}
return m;
}
int testSwapNode(int base, int target, int testValue, MinNode using, int m, int cost) {
int from = (base == 0 ? 0 : base - 1);
int to = (target == nodeCount - 1 ? nodeCount - 1 : target + 1);
N[base].count += testValue;
N[target].count -= testValue;
for (int i = from; i <= to; i++)
updateMinTo3(i);
Root.update(from == 0 ? 0 : from - 1, to == nodeCount - 1 ? to : to + 1, 0, nodeCount - 1);
m = Math.min(m, cost + Root.minTo3[using.type]);
N[base].count -= testValue;
N[target].count += testValue;
for (int i = from; i <= to; i++)
updateMinTo3(i);
return m;
}
private void updateMinTo3(int i) {
MinNode n = N[i];
int l = (i > 1) ? N[i - 1].count : BIG_NUMBER;
int r = (i < nodeCount - 2) ? N[i + 1].count : BIG_NUMBER;
n.minTo3[n.type] = BIG_NUMBER;
if (n.count == 1 && i > 1 && i < nodeCount - 2)
n.minTo3[n.type] = l + r;
else if (n.count == 2)
n.minTo3[n.type] = Math.min(l, r);
else if (n.count > 2)
n.minTo3[n.type] = 0;
}
}
The following issues have been detected: timeout errors.
No three consecutive letters in S are equal. Score x 2.
running time: 1.496 sec., time limit: 0.768 sec.
Big performance package. Score x 2.
running time: 1.448 sec., time limit: 0.848 sec.