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.
<Here is the solution that got 100 out of 100 in codility.com. Please read about prefix sums to understand the solution:
public static int[] solveGenomicRange(String S, int[] P, int[] Q) {
//used jagged array to hold the prefix sums of each A, C and G genoms
//we don't need to get prefix sums of T, you will see why.
int[][] genoms = new int[3][S.length()+1];
//if the char is found in the index i, then we set it to be 1 else they are 0
//3 short values are needed for this reason
short a, c, g;
for (int i=0; i<S.length(); i++) {
a = 0; c = 0; g = 0;
if ('A' == (S.charAt(i))) {
a=1;
}
if ('C' == (S.charAt(i))) {
c=1;
}
if ('G' == (S.charAt(i))) {
g=1;
}
//here we calculate prefix sums. To learn what's prefix sums look at here https://codility.com/media/train/3-PrefixSums.pdf
genoms[0][i+1] = genoms[0][i] + a;
genoms[1][i+1] = genoms[1][i] + c;
genoms[2][i+1] = genoms[2][i] + g;
}
int[] result = new int[P.length];
//here we go through the provided P[] and Q[] arrays as intervals
for (int i=0; i<P.length; i++) {
int fromIndex = P[i];
//we need to add 1 to Q[i],
//because our genoms[0][0], genoms[1][0] and genoms[2][0]
//have 0 values by default, look above genoms[0][i+1] = genoms[0][i] + a;
int toIndex = Q[i]+1;
if (genoms[0][toIndex] - genoms[0][fromIndex] > 0) {
result[i] = 1;
} else if (genoms[1][toIndex] - genoms[1][fromIndex] > 0) {
result[i] = 2;
} else if (genoms[2][toIndex] - genoms[2][fromIndex] > 0) {
result[i] = 3;
} else {
result[i] = 4;
}
}
return result;
}
Here is a C# solution, the basic idea is pretty much the same as the other answers, but it may be cleaner:
using System;
class Solution
{
public int[] solution(string S, int[] P, int[] Q)
{
int N = S.Length;
int M = P.Length;
char[] chars = {'A','C','G','T'};
//Calculate accumulates
int[,] accum = new int[3, N+1];
for (int i = 0; i <= 2; i++)
{
for (int j = 0; j < N; j++)
{
if(S[j] == chars[i]) accum[i, j+1] = accum[i, j] + 1;
else accum[i, j+1] = accum[i, j];
}
}
//Get minimal nucleotides for the given ranges
int diff;
int[] minimums = new int[M];
for (int i = 0; i < M; i++)
{
minimums[i] = 4;
for (int j = 0; j <= 2; j++)
{
diff = accum[j, Q[i]+1] - accum[j, P[i]];
if (diff > 0)
{
minimums[i] = j+1;
break;
}
}
}
return minimums;
}
}
Here is my solution. Got %100 . Of course I needed to first check and study a little bit prefix sums.
public int[] solution(String S, int[] P, int[] Q){
int[] result = new int[P.length];
int[] factor1 = new int[S.length()];
int[] factor2 = new int[S.length()];
int[] factor3 = new int[S.length()];
int[] factor4 = new int[S.length()];
int factor1Sum = 0;
int factor2Sum = 0;
int factor3Sum = 0;
int factor4Sum = 0;
for(int i=0; i<S.length(); i++){
switch (S.charAt(i)) {
case 'A':
factor1Sum++;
break;
case 'C':
factor2Sum++;
break;
case 'G':
factor3Sum++;
break;
case 'T':
factor4Sum++;
break;
default:
break;
}
factor1[i] = factor1Sum;
factor2[i] = factor2Sum;
factor3[i] = factor3Sum;
factor4[i] = factor4Sum;
}
for(int i=0; i<P.length; i++){
int start = P[i];
int end = Q[i];
if(start == 0){
if(factor1[end] > 0){
result[i] = 1;
}else if(factor2[end] > 0){
result[i] = 2;
}else if(factor3[end] > 0){
result[i] = 3;
}else{
result[i] = 4;
}
}else{
if(factor1[end] > factor1[start-1]){
result[i] = 1;
}else if(factor2[end] > factor2[start-1]){
result[i] = 2;
}else if(factor3[end] > factor3[start-1]){
result[i] = 3;
}else{
result[i] = 4;
}
}
}
return result;
}
This program has got score 100 and performance wise has got an edge over other java codes listed above!
The code can be found here.
public class GenomicRange {
final int Index_A=0, Index_C=1, Index_G=2, Index_T=3;
final int A=1, C=2, G=3, T=4;
public static void main(String[] args) {
GenomicRange gen = new GenomicRange();
int[] M = gen.solution( "GACACCATA", new int[] { 0,0,4,7 } , new int[] { 8,2,5,7 } );
System.out.println(Arrays.toString(M));
}
public int[] solution(String S, int[] P, int[] Q) {
int[] M = new int[P.length];
char[] charArr = S.toCharArray();
int[][] occCount = new int[3][S.length()+1];
int charInd = getChar(charArr[0]);
if(charInd!=3) {
occCount[charInd][1]++;
}
for(int sInd=1; sInd<S.length(); sInd++) {
charInd = getChar(charArr[sInd]);
if(charInd!=3)
occCount[charInd][sInd+1]++;
occCount[Index_A][sInd+1]+=occCount[Index_A][sInd];
occCount[Index_C][sInd+1]+=occCount[Index_C][sInd];
occCount[Index_G][sInd+1]+=occCount[Index_G][sInd];
}
for(int i=0;i<P.length;i++) {
int a,c,g;
if(Q[i]+1>=occCount[0].length) continue;
a = occCount[Index_A][Q[i]+1] - occCount[Index_A][P[i]];
c = occCount[Index_C][Q[i]+1] - occCount[Index_C][P[i]];
g = occCount[Index_G][Q[i]+1] - occCount[Index_G][P[i]];
M[i] = a>0? A : c>0 ? C : g>0 ? G : T;
}
return M;
}
private int getChar(char c) {
return ((c=='A') ? Index_A : ((c=='C') ? Index_C : ((c=='G') ? Index_G : Index_T)));
}
}
In ruby (100/100)
def interval_sum x,y,p
p[y+1] - p[x]
end
def solution(s,p,q)
#Hash of arrays with prefix sums
p_sums = {}
respuesta = []
%w(A C G T).each do |letter|
p_sums[letter] = Array.new s.size+1, 0
end
(0...s.size).each do |count|
%w(A C G T).each do |letter|
p_sums[letter][count+1] = p_sums[letter][count]
end if count > 0
case s[count]
when 'A'
p_sums['A'][count+1] += 1
when 'C'
p_sums['C'][count+1] += 1
when 'G'
p_sums['G'][count+1] += 1
when 'T'
p_sums['T'][count+1] += 1
end
end
(0...p.size).each do |count|
x = p[count]
y = q[count]
if interval_sum(x, y, p_sums['A']) > 0 then
respuesta << 1
next
end
if interval_sum(x, y, p_sums['C']) > 0 then
respuesta << 2
next
end
if interval_sum(x, y, p_sums['G']) > 0 then
respuesta << 3
next
end
if interval_sum(x, y, p_sums['T']) > 0 then
respuesta << 4
next
end
end
respuesta
end
This is a Swift 4 solution to the same problem. It is based on @codebusta's solution above:
public func solution(_ S : inout String, _ P : inout [Int], _ Q : inout [Int]) -> [Int] {
var impacts = [Int]()
var prefixSum = [[Int]]()
for _ in 0..<3 {
let array = Array(repeating: 0, count: S.count + 1)
prefixSum.append(array)
}
for (index, character) in S.enumerated() {
var a = 0
var c = 0
var g = 0
switch character {
case "A":
a = 1
case "C":
c = 1
case "G":
g = 1
default:
break
}
prefixSum[0][index + 1] = prefixSum[0][index] + a
prefixSum[1][index + 1] = prefixSum[1][index] + c
prefixSum[2][index + 1] = prefixSum[2][index] + g
}
for tuple in zip(P, Q) {
if prefixSum[0][tuple.1 + 1] - prefixSum[0][tuple.0] > 0 {
impacts.append(1)
}
else if prefixSum[1][tuple.1 + 1] - prefixSum[1][tuple.0] > 0 {
impacts.append(2)
}
else if prefixSum[2][tuple.1 + 1] - prefixSum[2][tuple.0] > 0 {
impacts.append(3)
}
else {
impacts.append(4)
}
}
return impacts
}