Skip to content
Related Articles

Related Articles

Improve Article

Solving Cryptarithmetic Puzzles | Set 2

  • Last Updated : 20 Oct, 2021
Geek Week

Given an array of strings, arr[] of size N and a string S, the task is to find if it is possible to map integers value in the range [0, 9] to every alphabet that occurs in the strings, such that the sum obtained after summing the numbers formed by encoding all strings in the array is equal to the number formed by the string S.

Examples:

Input: arr[][] = {“SEND”, “MORE”}, S = “MONEY”
Output: Yes
Explanation: 
One of the possible ways is:

  1. Map the characters as the following, S’→ 9, ‘E’→5, ‘N’→6, ‘D’→7, ‘M’→1, ‘O’→0, ‘R’→8, ‘Y’→2.
  2. Now, after encoding the strings “SEND”, “MORE”, and “MONEY”, modifies to 9567, 1085 and 10652 respectively.
  3. Thus, the sum of the values of “SEND” and “MORE” is equal to (9567+1085 = 10652), which is equal to the value of the string “MONEY”.

Therefore, print “Yes”.

Input: arr[][] = {“SIX”, “SEVEN”, “SEVEN”}, S = “TWENTY”
Output: Yes
Explanation: 
One of the possible ways is:



  1. Map the characters as the following, ‘S’→ 6, ‘I’→5, ‘X’→0, ‘E’→8, ‘V’→7, ‘N’→2, ‘T’→1, ‘W’→’3’, ‘Y’→4.
  2. Now, after encoding the strings “SIX”, “SEVEN”, and “TWENTY”, modifies to 650, 68782 and 138214 respectively.
  3. Thus, the sum of the values of “SIX”, “SEVEN”, and “SEVEN” is equal to (650+ 68782+ 68782 = 138214), which is equal to the value of the string “TWENTY”.

Therefore, print “Yes”.

Set 1 of this article has been discussed here in which the array of strings is of size 2.

Approach: The given problem can be solved using Backtracking. Follow the steps below to solve the problem:

  • Initialize three, arrays say mp[26], Hash[26], and CharAtfront[26] to store the mapped value of the alphabet, the sum of the position values of an alphabet in every string, and if a character is at the starting index of any string.
  • Initialize an array used[10] to store if a number is mapped to any alphabet or not.
  • Initialize a StringBuffer say unique to store the string with every occurred alphabet once.
  • Assign -1 to every array element of mp.
  • Iterate over the array arr[] using the variable i and perform the following operations:
    • Store the length of the string, arr[i] in a variable M.
    • Iterate over the string, arr[i] using the variable j and perform the following operations:
      • If mp[arr[i][j] – ‘A’] is -1, then append the arr[i][j] in uniq and assign 0 to mp[arr[i][j]-‘A].
      • Now increment the value of Hash[arr[i][j] -‘A’] by 10(M-j-1).
      • If arr[i].length() > 1 and j is 0 then mark true at arr[i][j] – ‘A’ in CharAtfront[].
  • Iterate over the string, S, and perform the same tasks as performed with every array Strings.
  • Fill -1 to every array element of mp.
  • Define a recursive function say solve(String word, int i, int S, int[] mp, int[] used) for backtracking:
    • If i is equal to word.length() then return true if S is 0. Otherwise, return false.
    • If mp[word[i]-‘A’] is not equal to -1 then recursively call the function solve(word, i+1, S+mp[word[i]-‘A’]*Hash[word[i]-‘A], mp, used) and then return the value returned by it.
    • Else, Initialize a variable X as false and iterate over the range [0, 9] using a variable j and perform the following operations:
      • Continue to the next iteration in the loop if any of the conditions are satisfied:
        • If used[j] is true.
        • If CharAtfront[word[i]-‘A’] is 1 and j is 0.
      • Now mark used[j] as true and assign j to mp[word[i]-‘A’].
      • After the above steps call the function solve(word, i+1, S + j * Hash[word[i]-‘A’], mp, used) and then perform bitwise OR of X with value returned by it.
      • Now mark used[j] as false and assign -1 to mp[word[i] – ‘A’] for backtracking.
    • Return the value of X.
  • Finally, after completing the above steps, if the value returned by solve(uniq, 0, 0, mp, used) is true, then print “Yes“. Otherwise, print “No“.

Below is the implementation of the above approach:

C++




// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
 
// Auxiliary Recursive function
// to perform backtracking
bool solve(string words, int i,
    int S, int mp[], int used[],
    int Hash[],
    int CharAtfront[])
{
    // If i is word.length
    if (i == words.length())
 
        // Return true if S is 0
        return (S == 0);
 
    // Stores the character at
    // index i
    char ch = words[i];
 
    // Stores the mapped value
    // of ch
    int val = mp[words[i] - 'A'];
 
    // If val is -1
    if (val != -1) {
 
        // Recursion
        return solve(words, i + 1,
                     S + val * Hash[ch - 'A'],
                     mp, used,
                     Hash, CharAtfront);
    }
 
    // Stores if there is any
    // possible solution
    bool x = false;
 
    // Iterate over the range
    for (int l = 0; l < 10; l++) {
 
        // If CharAtfront[ch-'A']
        // is true and l is 0
        if (CharAtfront[ch - 'A'] == 1
            && l == 0)
            continue;
 
        // If used[l] is true
        if (used[l] == 1)
            continue;
 
        // Assign l to ch
        mp[ch - 'A'] = l;
 
        // Marked l as used
        used[l] = 1;
 
        // Recursive function call
        x |= solve(words, i + 1,
                   S + l * Hash[ch - 'A'],
                   mp, used, Hash, CharAtfront);
 
        // Backtrack
        mp[ch - 'A'] = -1;
 
        // Unset used[l]
        used[l] = 0;
    }
 
    // Return the value of x;
    return x;
}
 
// Function to check if the
// assignment of digits to
// chacracters is possible
bool isSolvable(string words[], string result, int N)
{
   
    // Stores the value
    // assigned to alphabets
    int mp[26];
 
    // Stores if a number
    // is assigned to any
    // character or not
    int used[10];
 
    // Stores the sum of position
    // value of a character
    // in every string
    int Hash[26];
 
    // Stores if a character
    // is at index 0 of any
    // string
    int CharAtfront[26];
 
    memset(mp, -1, sizeof(mp));
    memset(used, -1, sizeof(used));
    memset(Hash, -1, sizeof(Hash));
    memset(CharAtfront, -1, sizeof(CharAtfront));
 
    // Stores the string formed
    // by concatanating every
    // occured character only
    // once
    string uniq = "";
 
    // Iterater over the array,
    // words
    for(int word = 0; word < N; word++) {
 
        // Iterate over the string,
        // word
        for (int i = 0; i < words[word].length(); i++) {
 
            // Stores the character
            // at ith position
            char ch = words[word][i];
 
            // Update Hash[ch-'A]
            Hash[ch - 'A'] += (int)pow(10, words[word].length() - i - 1);
 
            // If mp[ch-'A'] is -1
            if (mp[ch - 'A'] == -1) {
 
                mp[ch - 'A'] = 0;
                uniq += (char)ch;
            }
 
            // If i is 0 and word
            // length is greater
            // than 1
            if (i == 0 && words[word].length() > 1) {
 
                CharAtfront[ch - 'A'] = 1;
            }
        }
    }
 
    // Iterate over the string result
    for (int i = 0; i < result.length(); i++) {
 
        char ch = result[i];
 
        Hash[ch - 'A'] -= (int)pow(10, result.length() - i - 1);
 
        // If mp[ch-'A] is -1
        if (mp[ch - 'A'] == -1) {
            mp[ch - 'A'] = 0;
            uniq += (char)ch;
        }
 
        // If i is 0 and length of
        // result is greater than 1
        if (i == 0 && result.length() > 1) {
            CharAtfront[ch - 'A'] = 1;
        }
    }
 
    memset(mp, -1, sizeof(mp));
 
    // Recursive call of the function
    return solve(uniq, 0, 0, mp, used, Hash, CharAtfront);
}
     
int main()
{
    // Input
    string arr[] = { "SIX", "SEVEN", "SEVEN" };
    string S = "TWENTY";
     
    int N = sizeof(arr)/sizeof(arr[0]);
  
    // Function Call
    if (isSolvable(arr, S, N))
        cout << "Yes";
    else
        cout << "No";
 
    return 0;
}
 
// This code is contributed by suresh07.

Java




// Java program for the above approach
import java.io.*;
import java.util.*;
 
class GFG {
 
    // Function to check if the
    // assignment of digits to
    // chacracters is possible
    public static boolean isSolvable(String[] words,
                                     String result)
    {
        // Stores the value
        // assigned to alphabets
        int mp[] = new int[26];
 
        // Stores if a number
        // is assigned to any
        // character or not
        int used[] = new int[10];
 
        // Stores the sum of position
        // value of a character
        // in every string
        int Hash[] = new int[26];
 
        // Stores if a character
        // is at index 0 of any
        // string
        int CharAtfront[] = new int[26];
 
        Arrays.fill(mp, -1);
        Arrays.fill(used, 0);
        Arrays.fill(Hash, 0);
        Arrays.fill(CharAtfront, 0);
 
        // Stores the string formed
        // by concatanating every
        // occured character only
        // once
        StringBuilder uniq = new StringBuilder();
 
        // Iterater over the array,
        // words
        for (String word : words) {
 
            // Iterate over the string,
            // word
            for (int i = 0; i < word.length(); i++) {
 
                // Stores the character
                // at ith position
                char ch = word.charAt(i);
 
                // Update Hash[ch-'A]
                Hash[ch - 'A'] += (int)Math.pow(
                    10, word.length() - i - 1);
 
                // If mp[ch-'A'] is -1
                if (mp[ch - 'A'] == -1) {
 
                    mp[ch - 'A'] = 0;
                    uniq.append((char)ch);
                }
 
                // If i is 0 and word
                // length is greater
                // than 1
                if (i == 0 && word.length() > 1) {
 
                    CharAtfront[ch - 'A'] = 1;
                }
            }
        }
 
        // Iterate over the string result
        for (int i = 0; i < result.length(); i++) {
 
            char ch = result.charAt(i);
 
            Hash[ch - 'A'] -= (int)Math.pow(
                10, result.length() - i - 1);
 
            // If mp[ch-'A] is -1
            if (mp[ch - 'A'] == -1) {
                mp[ch - 'A'] = 0;
                uniq.append((char)ch);
            }
 
            // If i is 0 and length of
            // result is greater than 1
            if (i == 0 && result.length() > 1) {
                CharAtfront[ch - 'A'] = 1;
            }
        }
 
        Arrays.fill(mp, -1);
 
        // Recursive call of the function
        return solve(uniq, 0, 0, mp, used, Hash,
                     CharAtfront);
    }
 
    // Auxiliary Recursive function
    // to perform backtracking
    public static boolean solve(
        StringBuilder words, int i,
        int S, int[] mp, int[] used,
        int[] Hash,
        int[] CharAtfront)
    {
        // If i is word.length
        if (i == words.length())
 
            // Return true if S is 0
            return (S == 0);
 
        // Stores the character at
        // index i
        char ch = words.charAt(i);
 
        // Stores the mapped value
        // of ch
        int val = mp[words.charAt(i) - 'A'];
 
        // If val is -1
        if (val != -1) {
 
            // Recursion
            return solve(words, i + 1,
                         S + val * Hash[ch - 'A'],
                         mp, used,
                         Hash, CharAtfront);
        }
 
        // Stores if there is any
        // possible solution
        boolean x = false;
 
        // Iterate over the range
        for (int l = 0; l < 10; l++) {
 
            // If CharAtfront[ch-'A']
            // is true and l is 0
            if (CharAtfront[ch - 'A'] == 1
                && l == 0)
                continue;
 
            // If used[l] is true
            if (used[l] == 1)
                continue;
 
            // Assign l to ch
            mp[ch - 'A'] = l;
 
            // Marked l as used
            used[l] = 1;
 
            // Recursive function call
            x |= solve(words, i + 1,
                       S + l * Hash[ch - 'A'],
                       mp, used, Hash, CharAtfront);
 
            // Backtrack
            mp[ch - 'A'] = -1;
 
            // Unset used[l]
            used[l] = 0;
        }
 
        // Return the value of x;
        return x;
    }
 
    // Driver Code
    public static void main(String[] args)
    {
        // Input
        String[] arr
            = { "SIX", "SEVEN", "SEVEN" };
        String S = "TWENTY";
 
        // Function Call
        if (isSolvable(arr, S))
            System.out.println("Yes");
        else
            System.out.println("No");
    }
}

Python3




# Python3 program for the above approach
 
# Function to check if the
# assignment of digits to
# chacracters is possible
def isSolvable(words, result):
    # Stores the value
    # assigned to alphabets
    mp = [-1]*(26)
     
    # Stores if a number
    # is assigned to any
    # character or not
    used = [0]*(10)
 
    # Stores the sum of position
    # value of a character
    # in every string
    Hash = [0]*(26)
 
    # Stores if a character
    # is at index 0 of any
    # string
    CharAtfront = [0]*(26)
 
    # Stores the string formed
    # by concatanating every
    # occured character only
    # once
    uniq = ""
 
    # Iterater over the array,
    # words
    for word in range(len(words)):
        # Iterate over the string,
        # word
        for i in range(len(words[word])):
            # Stores the character
            # at ith position
            ch = words[word][i]
 
            # Update Hash[ch-'A]
            Hash[ord(ch) - ord('A')] += pow(10, len(words[word]) - i - 1)
 
            # If mp[ch-'A'] is -1
            if mp[ord(ch) - ord('A')] == -1:
                mp[ord(ch) - ord('A')] = 0
                uniq += str(ch)
 
            # If i is 0 and word
            # length is greater
            # than 1
            if i == 0 and len(words[word]) > 1:
                CharAtfront[ord(ch) - ord('A')] = 1
 
    # Iterate over the string result
    for i in range(len(result)):
        ch = result[i]
 
        Hash[ord(ch) - ord('A')] -= pow(10, len(result) - i - 1)
 
        # If mp[ch-'A] is -1
        if mp[ord(ch) - ord('A')] == -1:
            mp[ord(ch) - ord('A')] = 0
            uniq += str(ch)
 
        # If i is 0 and length of
        # result is greater than 1
        if i == 0 and len(result) > 1:
            CharAtfront[ord(ch) - ord('A')] = 1
 
    mp = [-1]*(26)
 
    # Recursive call of the function
    return True
 
# Auxiliary Recursive function
# to perform backtracking
def solve(words, i, S, mp, used, Hash, CharAtfront):
    # If i is word.length
    if i == len(words):
        # Return true if S is 0
        return S == 0
 
    # Stores the character at
    # index i
    ch = words[i]
 
    # Stores the mapped value
    # of ch
    val = mp[ord(words[i]) - ord('A')]
 
    # If val is -1
    if val != -1:
        # Recursion
        return solve(words, i + 1, S + val * Hash[ord(ch) - ord('A')], mp, used, Hash, CharAtfront)
 
    # Stores if there is any
    # possible solution
    x = False
 
    # Iterate over the range
    for l in range(10):
        # If CharAtfront[ch-'A']
        # is true and l is 0
        if CharAtfront[ord(ch) - ord('A')] == 1 and l == 0:
            continue
 
        # If used[l] is true
        if used[l] == 1:
            continue
 
        # Assign l to ch
        mp[ord(ch) - ord('A')] = l
 
        # Marked l as used
        used[l] = 1
 
        # Recursive function call
        x |= solve(words, i + 1, S + l * Hash[ord(ch) - ord('A')], mp, used, Hash, CharAtfront)
 
        # Backtrack
        mp[ord(ch) - ord('A')] = -1
 
        # Unset used[l]
        used[l] = 0
 
    # Return the value of x;
    return x
 
arr = [ "SIX", "SEVEN", "SEVEN" ]
S = "TWENTY"
 
# Function Call
if isSolvable(arr, S):
    print("Yes")
else:
    print("No")
     
    # This code is contributed by mukesh07.

C#




// C# program for the above approach
using System;
class GFG {
     
    // Function to check if the
    // assignment of digits to
    // chacracters is possible
    static bool isSolvable(string[] words, string result)
    {
        // Stores the value
        // assigned to alphabets
        int[] mp = new int[26];
   
        // Stores if a number
        // is assigned to any
        // character or not
        int[] used = new int[10];
   
        // Stores the sum of position
        // value of a character
        // in every string
        int[] Hash = new int[26];
   
        // Stores if a character
        // is at index 0 of any
        // string
        int[] CharAtfront = new int[26];
   
        Array.Fill(mp, -1);
        Array.Fill(used, 0);
        Array.Fill(Hash, 0);
        Array.Fill(CharAtfront, 0);
   
        // Stores the string formed
        // by concatanating every
        // occured character only
        // once
        string uniq = "";
   
        // Iterater over the array,
        // words
        foreach(string word in words) {
   
            // Iterate over the string,
            // word
            for (int i = 0; i < word.Length; i++) {
   
                // Stores the character
                // at ith position
                char ch = word[i];
   
                // Update Hash[ch-'A]
                Hash[ch - 'A'] += (int)Math.Pow(10, word.Length - i - 1);
   
                // If mp[ch-'A'] is -1
                if (mp[ch - 'A'] == -1) {
   
                    mp[ch - 'A'] = 0;
                    uniq += (char)ch;
                }
   
                // If i is 0 and word
                // length is greater
                // than 1
                if (i == 0 && word.Length > 1) {
   
                    CharAtfront[ch - 'A'] = 1;
                }
            }
        }
   
        // Iterate over the string result
        for (int i = 0; i < result.Length; i++) {
   
            char ch = result[i];
   
            Hash[ch - 'A'] -= (int)Math.Pow(10, result.Length - i - 1);
   
            // If mp[ch-'A] is -1
            if (mp[ch - 'A'] == -1) {
                mp[ch - 'A'] = 0;
                uniq += (char)ch;
            }
   
            // If i is 0 and length of
            // result is greater than 1
            if (i == 0 && result.Length > 1) {
                CharAtfront[ch - 'A'] = 1;
            }
        }
   
        Array.Fill(mp, -1);
   
        // Recursive call of the function
        return solve(uniq, 0, 0, mp, used, Hash, CharAtfront);
    }
   
    // Auxiliary Recursive function
    // to perform backtracking
    public static bool solve(string words, int i,
        int S, int[] mp, int[] used,
        int[] Hash,
        int[] CharAtfront)
    {
        // If i is word.length
        if (i == words.Length)
   
            // Return true if S is 0
            return (S == 0);
   
        // Stores the character at
        // index i
        char ch = words[i];
   
        // Stores the mapped value
        // of ch
        int val = mp[words[i] - 'A'];
   
        // If val is -1
        if (val != -1) {
   
            // Recursion
            return solve(words, i + 1,
                         S + val * Hash[ch - 'A'],
                         mp, used,
                         Hash, CharAtfront);
        }
   
        // Stores if there is any
        // possible solution
        bool x = false;
   
        // Iterate over the range
        for (int l = 0; l < 10; l++) {
   
            // If CharAtfront[ch-'A']
            // is true and l is 0
            if (CharAtfront[ch - 'A'] == 1
                && l == 0)
                continue;
   
            // If used[l] is true
            if (used[l] == 1)
                continue;
   
            // Assign l to ch
            mp[ch - 'A'] = l;
   
            // Marked l as used
            used[l] = 1;
   
            // Recursive function call
            x |= solve(words, i + 1,
                       S + l * Hash[ch - 'A'],
                       mp, used, Hash, CharAtfront);
   
            // Backtrack
            mp[ch - 'A'] = -1;
   
            // Unset used[l]
            used[l] = 0;
        }
   
        // Return the value of x;
        return x;
    }
     
  static void Main()
  {
     
    // Input
    string[] arr = { "SIX", "SEVEN", "SEVEN" };
    string S = "TWENTY";
 
    // Function Call
    if (isSolvable(arr, S))
        Console.Write("Yes");
    else
        Console.Write("No");
  }
}
 
// This code is contributed by divyesh072019.

Javascript




<script>
    // Javascript program for the above approach
     
    // Function to check if the
    // assignment of digits to
    // chacracters is possible
    function isSolvable(words, result)
    {
        // Stores the value
        // assigned to alphabets
        let mp = new Array(26);
    
        // Stores if a number
        // is assigned to any
        // character or not
        let used = new Array(10);
    
        // Stores the sum of position
        // value of a character
        // in every string
        let Hash = new Array(26);
    
        // Stores if a character
        // is at index 0 of any
        // string
        let CharAtfront = new Array(26);
    
        mp.fill(-1);
        used.fill(0);
        Hash.fill(0);
        CharAtfront.fill(0);
    
        // Stores the string formed
        // by concatanating every
        // occured character only
        // once
        let uniq = "";
    
        // Iterater over the array,
        // words
        for(let word = 0; word < words.length; word++) {
    
            // Iterate over the string,
            // word
            for (let i = 0; i < words[word].length; i++) {
    
                // Stores the character
                // at ith position
                let ch = words[word][i];
    
                // Update Hash[ch-'A]
                Hash[ch.charCodeAt() - 'A'.charCodeAt()] += Math.pow(10, words[word].length - i - 1);
    
                // If mp[ch-'A'] is -1
                if (mp[ch.charCodeAt() - 'A'.charCodeAt()] == -1) {
    
                    mp[ch.charCodeAt() - 'A'.charCodeAt()] = 0;
                    uniq += String.fromCharCode(ch);
                }
    
                // If i is 0 and word
                // length is greater
                // than 1
                if (i == 0 && words[word].length > 1) {
    
                    CharAtfront[ch.charCodeAt() - 'A'.charCodeAt()] = 1;
                }
            }
        }
    
        // Iterate over the string result
        for (let i = 0; i < result.length; i++) {
    
            let ch = result[i];
    
            Hash[ch.charCodeAt() - 'A'.charCodeAt()] -= Math.pow(10, result.length - i - 1);
    
            // If mp[ch-'A] is -1
            if (mp[ch.charCodeAt() - 'A'.charCodeAt()] == -1) {
                mp[ch.charCodeAt() - 'A'.charCodeAt()] = 0;
                uniq += String.fromCharCode(ch);
            }
    
            // If i is 0 and length of
            // result is greater than 1
            if (i == 0 && result.length > 1) {
                CharAtfront[ch.charCodeAt() - 'A'.charCodeAt()] = 1;
            }
        }
    
        mp.fill(-1);
    
        // Recursive call of the function
        return solve(uniq, 0, 0, mp, used, Hash, CharAtfront);
    }
    
    // Auxiliary Recursive function
    // to perform backtracking
    function solve(words, i, S, mp, used, Hash, CharAtfront)
    {
        // If i is word.length
        if (i == words.length)
    
            // Return true if S is 0
            return (S == 0);
    
        // Stores the character at
        // index i
        let ch = words[i];
    
        // Stores the mapped value
        // of ch
        let val = mp[words[i].charCodeAt() - 'A'.charCodeAt()];
    
        // If val is -1
        if (val != -1) {
    
            // Recursion
            return solve(words, i + 1, S + val * Hash[ch.charCodeAt() - 'A'.charCodeAt()], mp, used, Hash, CharAtfront);
        }
    
        // Stores if there is any
        // possible solution
        let x = false;
    
        // Iterate over the range
        for (let l = 0; l < 10; l++) {
    
            // If CharAtfront[ch-'A']
            // is true and l is 0
            if (CharAtfront[ch.charCodeAt() - 'A'.charCodeAt()] == 1
                && l == 0)
                continue;
    
            // If used[l] is true
            if (used[l] == 1)
                continue;
    
            // Assign l to ch
            mp[ch.charCodeAt() - 'A'.charCodeAt()] = l;
    
            // Marked l as used
            used[l] = 1;
    
            // Recursive function call
            x |= solve(words, i + 1,
                       S + l * Hash[ch.charCodeAt() - 'A'.charCodeAt()],
                       mp, used, Hash, CharAtfront);
    
            // Backtrack
            mp[ch.charCodeAt() - 'A'.charCodeAt()] = -1;
    
            // Unset used[l]
            used[l] = 0;
        }
    
        // Return the value of x;
        return x;
    }
     
    // Input
    let arr = [ "SIX", "SEVEN", "SEVEN" ];
    let S = "TWENTY";
  
    // Function Call
    if (!isSolvable(arr, S))
        document.write("Yes");
    else
        document.write("No");
     
    // This code is contributed by decode2207.
</script>
Output
Yes

Time Complexity: O(N*M+10!), where M is the length of the largest string.
Auxiliary Space: O(26)

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.  To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

In case you wish to attend live classes with experts, please refer DSA Live Classes for Working Professionals and Competitive Programming Live for Students.




My Personal Notes arrow_drop_up
Recommended Articles
Page :