The task is:
A non-empty zero-indexed string S is given. String S consists of N characters from the set of upper-case English letters A, C, G, T.
<Python Solution with explanation
The idea is to hold an auxiliary array per nucleotide X, with position i (ignoring zero) is how many times X has occurred as of now. And so if we need the number of occurrences of X from position f to position t, we could take the following equation:
aux(t) - aux(f)
Time complexity is:
O(N+M)
def solution(S, P, Q):
n = len(S)
m = len(P)
aux = [[0 for i in range(n+1)] for i in [0,1,2]]
for i,c in enumerate(S):
aux[0][i+1] = aux[0][i] + ( c == 'A' )
aux[1][i+1] = aux[1][i] + ( c == 'C' )
aux[2][i+1] = aux[2][i] + ( c == 'G' )
result = []
for i in range(m):
fromIndex , toIndex = P[i] , Q[i] +1
if aux[0][toIndex] - aux[0][fromIndex] > 0:
r = 1
elif aux[1][toIndex] - aux[1][fromIndex] > 0:
r = 2
elif aux[2][toIndex] - aux[2][fromIndex] > 0:
r = 3
else:
r = 4
result.append(r)
return result
static public int[] solution(String S, int[] P, int[] Q) {
// write your code in Java SE 8
int A[] = new int[S.length() + 1], C[] = new int[S.length() + 1], G[] = new int[S.length() + 1];
int last_a = 0, last_c = 0, last_g = 0;
int results[] = new int[P.length];
int p = 0, q = 0;
for (int i = S.length() - 1; i >= 0; i -= 1) {
switch (S.charAt(i)) {
case 'A': {
last_a += 1;
break;
}
case 'C': {
last_c += 1;
break;
}
case 'G': {
last_g += 1;
break;
}
}
A[i] = last_a;
G[i] = last_g;
C[i] = last_c;
}
for (int i = 0; i < P.length; i++) {
p = P[i];
q = Q[i];
if (A[p] - A[q + 1] > 0) {
results[i] = 1;
} else if (C[p] - C[q + 1] > 0) {
results[i] = 2;
} else if (G[p] - G[q + 1] > 0) {
results[i] = 3;
} else {
results[i] = 4;
}
}
return results;
}
Here's a simple javascript solution which got 100%.
function solution(S, P, Q) {
var A = [];
var C = [];
var G = [];
var T = [];
var result = [];
var i = 0;
S.split('').forEach(function(a) {
if (a === 'A') {
A.push(i);
} else if (a === 'C') {
C.push(i);
} else if (a === 'G') {
G.push(i);
} else {
T.push(i);
}
i++;
});
function hasNucl(typeArray, start, end) {
return typeArray.some(function(a) {
return a >= P[j] && a <= Q[j];
});
}
for(var j=0; j<P.length; j++) {
if (hasNucl(A, P[j], P[j])) {
result.push(1)
} else if (hasNucl(C, P[j], P[j])) {
result.push(2);
} else if (hasNucl(G, P[j], P[j])) {
result.push(3);
} else {
result.push(4);
}
}
return result;
}
simple php 100/100 solution
function solution($S, $P, $Q) {
$result = array();
for ($i = 0; $i < count($P); $i++) {
$from = $P[$i];
$to = $Q[$i];
$length = $from >= $to ? $from - $to + 1 : $to - $from + 1;
$new = substr($S, $from, $length);
if (strpos($new, 'A') !== false) {
$result[$i] = 1;
} else {
if (strpos($new, 'C') !== false) {
$result[$i] = 2;
} else {
if (strpos($new, 'G') !== false) {
$result[$i] = 3;
} else {
$result[$i] = 4;
}
}
}
}
return $result;
}
Here's 100% Scala solution:
def solution(S: String, P: Array[Int], Q: Array[Int]): Array[Int] = {
val resp = for(ind <- 0 to P.length-1) yield {
val sub= S.substring(P(ind),Q(ind)+1)
var factor = 4
if(sub.contains("A")) {factor=1}
else{
if(sub.contains("C")) {factor=2}
else{
if(sub.contains("G")) {factor=3}
}
}
factor
}
return resp.toArray
}
And performance: https://codility.com/demo/results/trainingEUR4XP-425/
pshemek's solution constrains itself to the space complexity (O(N)) - even with the 2-d array and the answer array because a constant (4) is used for the 2-d array. That solution also fits in with the computational complexity - whereas mine is O (N^2) - though the actual computational complexity is much lower because it skips over entire ranges that include minimal values.
I gave it a try - but mine ends up using more space - but makes more intuitive sense to me (C#):
public static int[] solution(String S, int[] P, int[] Q)
{
const int MinValue = 1;
Dictionary<char, int> stringValueTable = new Dictionary<char,int>(){ {'A', 1}, {'C', 2}, {'G', 3}, {'T', 4} };
char[] inputArray = S.ToCharArray();
int[,] minRangeTable = new int[S.Length, S.Length]; // The meaning of this table is [x, y] where x is the start index and y is the end index and the value is the min range - if 0 then it is the min range (whatever that is)
for (int startIndex = 0; startIndex < S.Length; ++startIndex)
{
int currentMinValue = 4;
int minValueIndex = -1;
for (int endIndex = startIndex; (endIndex < S.Length) && (minValueIndex == -1); ++endIndex)
{
int currentValue = stringValueTable[inputArray[endIndex]];
if (currentValue < currentMinValue)
{
currentMinValue = currentValue;
if (currentMinValue == MinValue) // We can stop iterating - because anything with this index in its range will always be minimal
minValueIndex = endIndex;
else
minRangeTable[startIndex, endIndex] = currentValue;
}
else
minRangeTable[startIndex, endIndex] = currentValue;
}
if (minValueIndex != -1) // Skip over this index - since it is minimal
startIndex = minValueIndex; // We would have a "+ 1" here - but the "auto-increment" in the for statement will get us past this index
}
int[] result = new int[P.Length];
for (int outputIndex = 0; outputIndex < result.Length; ++outputIndex)
{
result[outputIndex] = minRangeTable[P[outputIndex], Q[outputIndex]];
if (result[outputIndex] == 0) // We could avoid this if we initialized our 2-d array with 1's
result[outputIndex] = 1;
}
return result;
}
In pshemek's answer - the "trick" in the second loop is simply that once you've determined you've found a range with the minimal value - you don't need to continue iterating. Not sure if that helps.