Count Equidistant DNA Sequences

Last Updated : 30 Sep, 2026

Given two DNA sequences s1 and s2, each of length n, consisting of characters from the set {A, T, G, C}, find the number of DNA sequences s3 of length n such that the Hamming distance between s1 and s3 equals the Hamming distance between s2 and s3 under the specified matching rules. Under these rules:

  • 'A' matches with 'A' and 'T'
  • 'T' matches with 'T' and 'A'
  • 'C' matches with 'C' and 'G'
  • 'G' matches with 'G' and 'C'

The Hamming distance between two sequences is the number of positions where the corresponding characters do not match. Since the answer can be large, return it modulo 109 + 7.

Examples:

Input: s1 = "A", s2 = "T"
Output: 4
Explanation: Both 'A' and 'T' belong to the same matching group. For any choice of character in s3, the Hamming distance to s1 equals that to s2.

Input: s1 = "AG", s2 = "CT"
Output: 8
Explanation: At both positions, the characters belong to different groups. Choosing one position to match s1 and one to match s2 balances the Hamming distance, yielding 8 valid sequences.

Try It Yourself
redirect icon

[Naive Approach] Generate Each Sequence - O(4^n . n) Time and O(n) Space

The idea is to generate all possible DNA sequences s3 of length n using the alphabet {'A', 'T', 'G', 'C'} via recursion/backtracking (total 4n possibilities).

For each generated sequence, compute the Hamming distance from s1 and s2 according to the matching rules. If both distances are equal, increment valid count.

Finally, we return the count modulo 109 + 7.

Note: This approach has an exponential time complexity of O(4n . n) and is only feasible for very small inputs (n ≤ 6).

C++
#include 
#include 
using namespace std;

const int MOD = 1000000007;

bool matches(char a, char b) {
    if (a == b) return true;
    if ((a == 'A' && b == 'T') || (a == 'T' && b == 'A')) return true;
    if ((a == 'C' && b == 'G') || (a == 'G' && b == 'C')) return true;
    return false;
}

void solve(int idx, string &s1, string &s2, string &s3, long long &count) {
    int n = s1.size();
    if (idx == n) {
        int dist1 = 0, dist2 = 0;
        for (int i = 0; i < n; i++) {
            if (!matches(s1[i], s3[i])) dist1++;
            if (!matches(s2[i], s3[i])) dist2++;
        }
        if (dist1 == dist2) {
            count = (count + 1) % MOD;
        }
        return;
    }

    char chars[] = {'A', 'T', 'G', 'C'};
    for (char c : chars) {
        s3.push_back(c);
        solve(idx + 1, s1, s2, s3, count);
        s3.pop_back();
    }
}

int getValidCount(string s1, string s2) {
    long long count = 0;
    string s3 = "";
    solve(0, s1, s2, s3, count);
    return count;
}

int main() {
    string s1 = "AG";
    string s2 = "CT";
    cout << getValidCount(s1, s2) << endl;
    return 0;
}
Java
public class GFG {
    private static final int MOD = 1000000007;
    private static long count = 0;

    private static boolean matches(char a, char b) {
        if (a == b) return true;
        if ((a == 'A' && b == 'T') || (a == 'T' && b == 'A')) return true;
        if ((a == 'C' && b == 'G') || (a == 'G' && b == 'C')) return true;
        return false;
    }

    private static void solve(int idx, String s1, String s2, StringBuilder s3) {
        int n = s1.length();
        if (idx == n) {
            int dist1 = 0, dist2 = 0;
            for (int i = 0; i < n; i++) {
                if (!matches(s1.charAt(i), s3.charAt(i))) dist1++;
                if (!matches(s2.charAt(i), s3.charAt(i))) dist2++;
            }
            if (dist1 == dist2) {
                count = (count + 1) % MOD;
            }
            return;
        }

        char[] chars = {'A', 'T', 'G', 'C'};
        for (char c : chars) {
            s3.append(c);
            solve(idx + 1, s1, s2, s3);
            s3.deleteCharAt(s3.length() - 1);
        }
    }

    public static int getValidCount(String s1, String s2) {
        count = 0;
        solve(0, s1, s2, new StringBuilder());
        return (int) count;
    }

    public static void main(String[] args) {
        String s1 = "AG";
        String s2 = "CT";
        System.out.println(getValidCount(s1, s2));
    }
}
Python
MOD = 1000000007

def matches(a, b):
    if a == b: return True
    if (a == 'A' and b == 'T') or (a == 'T' and b == 'A'): return True
    if (a == 'C' and b == 'G') or (a == 'G' and b == 'C'): return True
    return False

def getValidCount(s1: str, s2: str) -> int:
    n = len(s1)
    count = 0

    def solve(idx, s3):
        nonlocal count
        if idx == n:
            dist1 = sum(1 for i in range(n) if not matches(s1[i], s3[i]))
            dist2 = sum(1 for i in range(n) if not matches(s2[i], s3[i]))
            if dist1 == dist2:
                count = (count + 1) % MOD
            return

        for c in ['A', 'T', 'G', 'C']:
            solve(idx + 1, s3 + c)

    solve(0, "")
    return count

if __name__ == "__main__":
    s1 = "AG"
    s2 = "CT"
    print(getValidCount(s1, s2))
C#
using System;
using System.Text;

public class GFG {
    private const int MOD = 1000000007;
    private static long count = 0;

    private static bool Matches(char a, char b) {
        if (a == b) return true;
        if ((a == 'A' && b == 'T') || (a == 'T' && b == 'A')) return true;
        if ((a == 'C' && b == 'G') || (a == 'G' && b == 'C')) return true;
        return false;
    }

    private static void Solve(int idx, string s1, string s2, StringBuilder s3) {
        int n = s1.Length;
        if (idx == n) {
            int dist1 = 0, dist2 = 0;
            for (int i = 0; i < n; i++) {
                if (!Matches(s1[i], s3[i])) dist1++;
                if (!Matches(s2[i], s3[i])) dist2++;
            }
            if (dist1 == dist2) {
                count = (count + 1) % MOD;
            }
            return;
        }

        char[] chars = { 'A', 'T', 'G', 'C' };
        foreach (char c in chars) {
            s3.Append(c);
            Solve(idx + 1, s1, s2, s3);
            s3.Remove(s3.Length - 1, 1);
        }
    }

    public static int getValidCount(string s1, string s2) {
        count = 0;
        Solve(0, s1, s2, new StringBuilder());
        return (int)count;
    }

    public static void Main(string[] args) {
        string s1 = "AG";
        string s2 = "CT";
        Console.WriteLine(getValidCount(s1, s2));
    }
}
JavaScript
const MOD = 1000000007;

function matches(a, b) {
    if (a === b) return true;
    if ((a === 'A' && b === 'T') || (a === 'T' && b === 'A')) return true;
    if ((a === 'C' && b === 'G') || (a === 'G' && b === 'C')) return true;
    return false;
}

function getValidCount(s1, s2) {
    let n = s1.length;
    let count = 0n;

    function solve(idx, s3) {
        if (idx === n) {
            let dist1 = 0, dist2 = 0;
            for (let i = 0; i < n; i++) {
                if (!matches(s1[i], s3[i])) dist1++;
                if (!matches(s2[i], s3[i])) dist2++;
            }
            if (dist1 === dist2) {
                count = (count + 1n) % BigInt(MOD);
            }
            return;
        }

        for (let c of ['A', 'T', 'G', 'C']) {
            solve(idx + 1, s3 + c);
        }
    }

    solve(0, "");
    return Number(count);
}

// Driver Code
const s1 = "AG";
const s2 = "CT";
console.log(getValidCount(s1, s2));

Output
8

[Better Approach] Dynamic Programming for Combinations - O(n^2) Time and O(n^2) Space

We compute binomial coefficients NCk using Pascal's Triangle (Dynamic Programming) up to N = n. Then, we analyze the string positions:

  1. Count equal positions (where characters belong to the same group) and diff positions (where they differ).
  2. If diff is odd, return 0.
  3. Otherwise, use our precomputed DP table to retrieve C(diff, diff/2), multiply by 4 for the differing parts and 4equal for matching parts.
C++
#include 
#include 
#include 
using namespace std;

const int MOD = 1000000007;

long long power(long long base, int exp) {
    long long res = 1;
    base %= MOD;
    while (exp > 0) {
        if (exp % 2 == 1) res = (res * base) % MOD;
        base = (base * base) % MOD;
        exp /= 2;
    }
    return res;
}

int getValidCount(string s1, string s2) {
    int n = s1.size();
    int equal = 0, diff = 0;

    for (int i = 0; i < n; i++) {
        bool g1 = (s1[i] == 'A' || s1[i] == 'T');
        bool g2 = (s2[i] == 'A' || s2[i] == 'T');
        if (g1 == g2) equal++;
        else diff++;
    }

    if (diff % 2 != 0) return 0;

    // Pascal's Triangle DP for Combinations
    vector<vector<long long>> dp(diff + 1, vector<long long>(diff + 1, 0));
    for (int i = 0; i <= diff; i++) {
        dp[i][0] = 1;
        for (int j = 1; j <= i; j++) {
            dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % MOD;
        }
    }

    long long combinations = dp[diff][diff / 2];
    long long res = (combinations * 4) % MOD;
    res = (res * power(4, equal)) % MOD;

    return res;
}

int main() {
    string s1 = "AG";
    string s2 = "CT";
    cout << getValidCount(s1, s2) << endl;
    return 0;
}
Java
public class GFG {
    private static final int MOD = 1000000007;

    private static long power(long base, int exp) {
        long res = 1;
        base %= MOD;
        while (exp > 0) {
            if (exp % 2 == 1) res = (res * base) % MOD;
            base = (base * base) % MOD;
            exp /= 2;
        }
        return res;
    }

    public static int getValidCount(String s1, String s2) {
        int n = s1.length();
        int equal = 0, diff = 0;

        for (int i = 0; i < n; i++) {
            boolean g1 = (s1.charAt(i) == 'A' || s1.charAt(i) == 'T');
            boolean g2 = (s2.charAt(i) == 'A' || s2.charAt(i) == 'T');
            if (g1 == g2) equal++;
            else diff++;
        }

        if (diff % 2 != 0) return 0;

        long[][] dp = new long[diff + 1][diff + 1];
        for (int i = 0; i <= diff; i++) {
            dp[i][0] = 1;
            for (int j = 1; j <= i; j++) {
                dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % MOD;
            }
        }

        long combinations = dp[diff][diff / 2];
        long res = (combinations * 4) % MOD;
        res = (res * power(4, equal)) % MOD;

        return (int) res;
    }

    public static void main(String[] args) {
        String s1 = "AG";
        String s2 = "CT";
        System.out.println(getValidCount(s1, s2));
    }
}
Python
MOD = 1000000007

def power(base, exp):
    res = 1
    base %= MOD
    while exp > 0:
        if exp % 2 == 1:
            res = (res * base) % MOD
        base = (base * base) % MOD
        exp //= 2
    return res

def getValidCount(s1: str, s2: str) -> int:
    n = len(s1)
    equal = 0
    diff = 0

    for i in range(n):
        g1 = (s1[i] == 'A' or s1[i] == 'T')
        g2 = (s2[i] == 'A' or s2[i] == 'T')
        if g1 == g2:
            equal += 1
        else:
            diff += 1

    if diff % 2 != 0:
        return 0

    dp = [[0] * (diff + 1) for _ in range(diff + 1)]
    for i in range(diff + 1):
        dp[i][0] = 1
        for j in range(1, i + 1):
            dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % MOD

    combinations = dp[diff][diff // 2]
    res = (combinations * 4) % MOD
    res = (res * power(4, equal)) % MOD

    return res

if __name__ == "__main__":
    s1 = "AG"
    s2 = "CT"
    print(getValidCount(s1, s2))
C#
using System;

public class GFG {
    private const int MOD = 1000000007;

    private static long Power(long baseVal, int exp) {
        long res = 1;
        baseVal %= MOD;
        while (exp > 0) {
            if (exp % 2 == 1) res = (res * baseVal) % MOD;
            baseVal = (baseVal * baseVal) % MOD;
            exp /= 2;
        }
        return res;
    }

    public static int getValidCount(string s1, string s2) {
        int n = s1.Length;
        int equal = 0, diff = 0;

        for (int i = 0; i < n; i++) {
            bool g1 = (s1[i] == 'A' || s1[i] == 'T');
            bool g2 = (s2[i] == 'A' || s2[i] == 'T');
            if (g1 == g2) equal++;
            else diff++;
        }

        if (diff % 2 != 0) return 0;

        long[,] dp = new long[diff + 1, diff + 1];
        for (int i = 0; i <= diff; i++) {
            dp[i, 0] = 1;
            for (int j = 1; j <= i; j++) {
                dp[i, j] = (dp[i - 1, j - 1] + dp[i - 1, j]) % MOD;
            }
        }

        long combinations = dp[diff, diff / 2];
        long res = (combinations * 4) % MOD;
        res = (res * Power(4, equal)) % MOD;

        return (int)res;
    }

    public static void Main(string[] args) {
        string s1 = "AG";
        string s2 = "CT";
        Console.WriteLine(getValidCount(s1, s2));
    }
}
JavaScript
const MOD = 1000000007;

function power(base, exp) {
    let res = 1n;
    let b = BigInt(base) % BigInt(MOD);
    let e = BigInt(exp);
    while (e > 0n) {
        if (e % 2n === 1n) res = (res * b) % BigInt(MOD);
        b = (b * b) % BigInt(MOD);
        e /= 2n;
    }
    return Number(res);
}

function getValidCount(s1, s2) {
    let n = s1.length;
    let equal = 0, diff = 0;

    for (let i = 0; i < n; i++) {
        let g1 = (s1[i] === 'A' || s1[i] === 'T');
        let g2 = (s2[i] === 'A' || s2[i] === 'T');
        if (g1 === g2) equal++;
        else diff++;
    }

    if (diff % 2 !== 0) return 0;

    let dp = Array.from({ length: diff + 1 }, () => new Array(diff + 1).fill(0n));
    for (let i = 0; i <= diff; i++) {
        dp[i][0] = 1n;
        for (let j = 1; j <= i; j++) {
            dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % BigInt(MOD);
        }
    }

    let combinations = dp[diff][Math.floor(diff / 2)];
    let res = Number((combinations * 4n) % BigInt(MOD));
    res = Number((BigInt(res) * BigInt(power(4, equal))) % BigInt(MOD));

    return res;
}

// Driver Code
const s1 = "AG";
const s2 = "CT";
console.log(getValidCount(s1, s2));

Output
8

[Expected Approach] Combinatorics & Group Matching - O(n) Time and O(n) Space

We analyze each character position independently based on the matching groups:

  1. Matching Groups: The bases are divided into two groups: {A, T} and {C, G}.
  2. Equal Positions (equal): At positions where characters of s1 and s2 belong to the same group, any choice of character in s3 contributes equally to both distances. There are 4 valid choices for each such position, yielding 4equal total combinations.
  3. Differing Positions (diff): At positions where s1 and s2 belong to different groups, balancing the Hamming distance requires that exactly half of these positions match s1 and the other half match s2. If diff is odd, it's impossible to balance, so the answer is 0. Otherwise, we choose diff/2 positions out of diff using combinations C(diff, diff/2), and multiply by the valid character choices.
C++
#include 
#include 
#include 

using namespace std;

const int MOD = 1000000007;

long long power(long long base, int exp) {
    long long res = 1;
    base %= MOD;
    while (exp > 0) {
        if (exp % 2 == 1) {
            res = (res * base) % MOD;
        }
        base = (base * base) % MOD;
        exp /= 2;
    }
    return res;
}

int modInverse(int n) {
    return power(n, MOD - 2);
}

int getValidCount(string &s1, string &s2) {
    int n = s1.size();
    int equal = 0;
    int diff = 0;

    for (int idx = 0; idx < n; idx++) {
        bool s1Group = (s1[idx] == 'A' || s1[idx] == 'T');
        bool s2Group = (s2[idx] == 'A' || s2[idx] == 'T');

        if (s1Group == s2Group) {
            equal++;
        } else {
            diff++;
        }
    }

    if (diff % 2 != 0) {
        return 0;
    }

    if (diff == 0) {
        return power(4, equal);
    }

    vector<int> fact(diff + 1, 1);
    vector<int> invFact(diff + 1, 1);

    for (int idx = 1; idx <= diff; idx++) {
        fact[idx] = (1LL * fact[idx - 1] * idx) % MOD;
    }

    invFact[diff] = modInverse(fact[diff]);
    for (int idx = diff - 1; idx >= 0; idx--) {
        invFact[idx] = (1LL * invFact[idx + 1] * (idx + 1)) % MOD;
    }

    int half = diff / 2;
    long long combinations = 1LL * fact[diff] * invFact[half] % MOD * invFact[half] % MOD;

    long long res = (combinations * 4) % MOD;
    res = (res * power(4, equal)) % MOD;

    return res;
}

int main() {
    string s1 = "AG";
    string s2 = "CT";
    cout << getValidCount(s1, s2) << endl; // Output: 8
    return 0;
}
Java
import java.util.*;

public class GFG {
    private static final int MOD = 1000000007;

    private static long power(long base, int exp) {
        long res = 1;
        base %= MOD;
        while (exp > 0) {
            if (exp % 2 == 1) {
                res = (res * base) % MOD;
            }
            base = (base * base) % MOD;
            exp /= 2;
        }
        return res;
    }

    private static int modInverse(int n) {
        return (int) power(n, MOD - 2);
    }

    public static int getValidCount(String s1, String s2) {
        int n = s1.length();
        int equal = 0;
        int diff = 0;

        for (int idx = 0; idx < n; idx++) {
            boolean s1Group = (s1.charAt(idx) == 'A' || s1.charAt(idx) == 'T');
            boolean s2Group = (s2.charAt(idx) == 'A' || s2.charAt(idx) == 'T');

            if (s1Group == s2Group) {
                equal++;
            } else {
                diff++;
            }
        }

        if (diff % 2 != 0) {
            return 0;
        }

        if (diff == 0) {
            return (int) power(4, equal);
        }

        int[] fact = new int[diff + 1];
        int[] invFact = new int[diff + 1];
        fact[0] = 1;

        for (int idx = 1; idx <= diff; idx++) {
            fact[idx] = (int) ((1L * fact[idx - 1] * idx) % MOD);
        }

        invFact[diff] = modInverse(fact[diff]);
        for (int idx = diff - 1; idx >= 0; idx--) {
            invFact[idx] = (int) ((1L * invFact[idx + 1] * (idx + 1)) % MOD);
        }

        int half = diff / 2;
        long combinations = 1L * fact[diff] * invFact[half] % MOD * invFact[half] % MOD;

        long res = (combinations * 4) % MOD;
        res = (res * power(4, equal)) % MOD;

        return (int) res;
    }

    public static void main(String[] args) {
        String s1 = "AG";
        String s2 = "CT";
        System.out.println(getValidCount(s1, s2)); // Output: 8
    }
}
Python
MOD = 1000000007

def power(base, exp):
    res = 1
    base %= MOD
    while exp > 0:
        if exp % 2 == 1:
            res = (res * base) % MOD
        base = (base * base) % MOD
        exp //= 2
    return res

def mod_inverse(n):
    return power(n, MOD - 2)

def getValidCount(s1: str, s2: str) -> int:
    n = len(s1)
    equal = 0
    diff = 0

    for idx in range(n):
        s1Group = (s1[idx] == 'A' or s1[idx] == 'T')
        s2Group = (s2[idx] == 'A' or s2[idx] == 'T')

        if s1Group == s2Group:
            equal += 1
        else:
            diff += 1

    if diff % 2 != 0:
        return 0

    if diff == 0:
        return power(4, equal)

    fact = [1] * (diff + 1)
    invFact = [1] * (diff + 1)

    for idx in range(1, diff + 1):
        fact[idx] = (fact[idx - 1] * idx) % MOD

    invFact[diff] = mod_inverse(fact[diff])
    for idx in range(diff - 1, -1, -1):
        invFact[idx] = (invFact[idx + 1] * (idx + 1)) % MOD

    half = diff // 2
    combinations = fact[diff] * invFact[half] % MOD * invFact[half] % MOD

    res = (combinations * 4) % MOD
    res = (res * power(4, equal)) % MOD

    return res

if __name__ == "__main__":
    s1 = "AG"
    s2 = "CT"
    print(getValidCount(s1, s2))  # Output: 8
C#
using System;

public class GFG {
    private const int MOD = 1000000007;

    private static long Power(long baseVal, int exp) {
        long res = 1;
        baseVal %= MOD;
        while (exp > 0) {
            if (exp % 2 == 1) {
                res = (res * baseVal) % MOD;
            }
            baseVal = (baseVal * baseVal) % MOD;
            exp /= 2;
        }
        return res;
    }

    private static int ModInverse(int n) {
        return (int)Power(n, MOD - 2);
    }

    public static int getValidCount(string s1, string s2) {
        int n = s1.Length;
        int equal = 0;
        int diff = 0;

        for (int idx = 0; idx < n; idx++) {
            bool s1Group = (s1[idx] == 'A' || s1[idx] == 'T');
            bool s2Group = (s2[idx] == 'A' || s2[idx] == 'T');

            if (s1Group == s2Group) {
                equal++;
            } else {
                diff++;
            }
        }

        if (diff % 2 != 0) {
            return 0;
        }

        if (diff == 0) {
            return (int)Power(4, equal);
        }

        int[] fact = new int[diff + 1];
        int[] invFact = new int[diff + 1];
        fact[0] = 1;

        for (int idx = 1; idx <= diff; idx++) {
            fact[idx] = (int)((1L * fact[idx - 1] * idx) % MOD);
        }

        invFact[diff] = ModInverse(fact[diff]);
        for (int idx = diff - 1; idx >= 0; idx--) {
            invFact[idx] = (int)((1L * invFact[idx + 1] * (idx + 1)) % MOD);
        }

        int half = diff / 2;
        long combinations = 1L * fact[diff] * invFact[half] % MOD * invFact[half] % MOD;

        long res = (combinations * 4) % MOD;
        res = (res * Power(4, equal)) % MOD;

        return (int)res;
    }

    public static void Main(string[] args) {
        string s1 = "AG";
        string s2 = "CT";
        Console.WriteLine(getValidCount(s1, s2)); // Output: 8
    }
}
JavaScript
const MOD = 1000000007;

function power(base, exp) {
    let res = 1n;
    let b = BigInt(base) % BigInt(MOD);
    let e = BigInt(exp);
    while (e > 0n) {
        if (e % 2n === 1n) {
            res = (res * b) % BigInt(MOD);
        }
        b = (b * b) % BigInt(MOD);
        e /= 2n;
    }
    return Number(res);
}

function modInverse(n) {
    return power(n, MOD - 2);
}

function getValidCount(s1, s2) {
    let n = s1.length;
    let equal = 0;
    let diff = 0;

    for (let idx = 0; idx < n; idx++) {
        let s1Group = (s1[idx] === 'A' || s1[idx] === 'T');
        let s2Group = (s2[idx] === 'A' || s2[idx] === 'T');

        if (s1Group === s2Group) {
            equal++;
        } else {
            diff++;
        }
    }

    if (diff % 2 !== 0) {
        return 0;
    }

    if (diff === 0) {
        return power(4, equal);
    }

    let fact = new Array(diff + 1).fill(1);
    let invFact = new Array(diff + 1).fill(1);

    for (let idx = 1; idx <= diff; idx++) {
        fact[idx] = Number((BigInt(fact[idx - 1]) * BigInt(idx)) % BigInt(MOD));
    }

    invFact[diff] = modInverse(fact[diff]);
    
    // Fixed: changed 'int idx' to 'let idx'
    for (let idx = diff - 1; idx >= 0; idx--) {
        invFact[idx] = Number((BigInt(invFact[idx + 1]) * BigInt(idx + 1)) % BigInt(MOD));
    }

    let half = Math.floor(diff / 2);
    let combinations = Number((BigInt(fact[diff]) * BigInt(invFact[half]) % BigInt(MOD)) * BigInt(invFact[half]) % BigInt(MOD));

    let res = Number((BigInt(combinations) * 4n) % BigInt(MOD));
    res = Number((BigInt(res) * BigInt(power(4, equal))) % BigInt(MOD));

    return res;
}

// Driver Code
const s1 = "AG";
const s2 = "CT";
console.log(getValidCount(s1, s2)); // Output: 8

Output
8
Comment