Open In App

Program to perform a letter frequency attack on a monoalphabetic substitution cipher

Improve
Improve
Like Article
Like
Save
Share
Report

Given a string S of size N representing a monoalphabetic cipher, the task is to print the top five possible plain texts that can be decrypted from the given monoalphabetic cipher using a letter frequency attack.

Examples:

Input: S = “ETAOINSHRDLCUMWFGYPBVKJXQZ”
Output: A SIMPLE MESSAGE
              B TJNQMF NFTTBHF
             A SIMPLE MESSAGE
             C UKORNG OGUUCIG
             C UKORNG OGUUCIG

Input: S = “ABCDEFGH”
Output: W OEILHA IAOOWCA
              J BRVYUN VNBBJPN
             C UKORNG OGUUCIG
             R JZDGCV DVJJRXV
             Y QGKNJC KCQQYEC

Approach: The problem can be solved based on the following observations:

  1. Frequency analysis is one of the known ciphertext attacks. It is based on the study of the frequency of letters or groups of letters in a ciphertext. In all languages, different letters are used with different frequencies.
  2. The frequency array attack is based on the observation that in an English text, not all letters occur with the same frequency.
  3. In the given problem, the string, T = “ETAOINSHRDLCUMWFGYPBVKJXQZ” is used for deciphering.
  4. Therefore, the idea is to find the difference between ith maximum occurring letter in the given string and the string T and then shift all the letters of the given string with that difference. The string obtained will be one of the possible decrypted strings.
     

Follow the steps below to solve the problem:

  • Initialize a string say T as “ETAOINSHRDLCUMWFGYPBVKJXQZ”.
  • Find the frequency of each character of the string S, and store it in a variable, say freq[].
  • Iterate over the range [0, 5] using the variable i and perform the following steps:
    • Find the ith most occurring element in the string S and store it in a variable, say ch.
    • Find the difference between the ch and ith character of the string T and store it in a variable, say x.
    • Iterate over the characters of string S, and shift all characters by x and then push the obtained string into an array plaintext[].
  • Finally, after the above steps, print the strings obtained in the array plaintext[].

Below is the implementation of the above approach:

C++




// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
 
// Function to decrypt a monoalphabetic
// substitution cipher using the letter
// frequency attack
void printString(string S, int N)
{
 
    // Stores final 5 possible deciphered
    // plaintext
    string plaintext[5];
 
    // Store the frequency of each letter in
    // cipher text
    int freq[26] = { 0 };
 
    // Stores the frequency of each letter
    // in cipher text in descending order
    int freqSorted[26];
 
    // Store which alphabet is used already
    int Used[26] = { 0 };
 
    // Traverse the string S
    for (int i = 0; i < N; i++) {
        if (S[i] != ' ') {
            freq[S[i] - 'A']++;
        }
    }
 
    // Copy the frequency array
    for (int i = 0; i < 26; i++) {
        freqSorted[i] = freq[i];
    }
 
    // Stores the string formed from concatenating
    // the english letters in the decreasing frequency
    // in the english language
    string T = "ETAOINSHRDLCUMWFGYPBVKJXQZ";
 
    // Sort the array in descending order
    sort(freqSorted, freqSorted + 26, greater<int>());
 
    // Iterate over the range [0, 5]
    for (int i = 0; i < 5; i++) {
 
        int ch = -1;
 
        // Iterate over the range [0, 26]
        for (int j = 0; j < 26; j++) {
 
            if (freqSorted[i] == freq[j] && Used[j] == 0) {
                Used[j] = 1;
                ch = j;
                break;
            }
        }
        if (ch == -1)
            break;
 
        // Store the numerical equivalent of letter at
        // ith index of array letter_frequency
        int x = T[i] - 'A';
 
        // Calculate the probable shift used
        // in monoalphabetic cipher
        x = x - ch;
 
        // Temporary string to generate one
        // plaintext at a time
        string curr = "";
 
        // Generate the probable ith plaintext
        // string using the shift calculated above
        for (int k = 0; k < N; k++) {
 
            // Insert whitespaces as it is
            if (S[k] == ' ') {
                curr += ' ';
                continue;
            }
 
            // Shift the kth letter of the
            // cipher by x
            int y = S[k] - 'A';
            y += x;
 
            if (y < 0)
                y += 26;
            if (y > 25)
                y -= 26;
 
            // Add the kth calculated/shifted
            // letter to temporary string
            curr += 'A' + y;
        }
 
        plaintext[i] = curr;
    }
 
    // Print the generated 5 possible plaintexts
    for (int i = 0; i < 5; i++) {
        cout << plaintext[i] << endl;
    }
}
 
// Driver Code
int main()
{
    // Given string
    string S = "B TJNQMF NFTTBHF";
    int N = S.length();
 
    // Function Call
    printString(S, N);
 
    return 0;
}


Java




// Java program for the above approach
import java.util.*;
 
class GFG{
 
// Function to decrypt a monoalphabetic
// substitution cipher using the letter
// frequency attack
static void printString(String S, int N)
{
 
    // Stores final 5 possible deciphered
    // plaintext
    String []plaintext = new String[5];
 
    // Store the frequency of each letter in
    // cipher text
    int freq[] = new int[26];
 
    // Stores the frequency of each letter
    // in cipher text in descending order
    int freqSorted[] = new int[26];
 
    // Store which alphabet is used already
    int Used[] = new int[26];
 
    // Traverse the String S
    for (int i = 0; i < N; i++) {
        if (S.charAt(i) != ' ') {
            freq[S.charAt(i) - 'A']++;
        }
    }
 
    // Copy the frequency array
    for (int i = 0; i < 26; i++) {
        freqSorted[i] = freq[i];
    }
 
    // Stores the String formed from concatenating
    // the english letters in the decreasing frequency
    // in the english language
    String T = "ETAOINSHRDLCUMWFGYPBVKJXQZ";
 
    // Sort the array in descending order
    Arrays.sort(freqSorted);
    freqSorted= reverse(freqSorted);
    // Iterate over the range [0, 5]
    for (int i = 0; i < 5; i++) {
 
        int ch = -1;
 
        // Iterate over the range [0, 26]
        for (int j = 0; j < 26; j++) {
 
            if (freqSorted[i] == freq[j] && Used[j] == 0) {
                Used[j] = 1;
                ch = j;
                break;
            }
        }
        if (ch == -1)
            break;
 
        // Store the numerical equivalent of letter at
        // ith index of array letter_frequency
        int x = T.charAt(i) - 'A';
         
        // Calculate the probable shift used
        // in monoalphabetic cipher
        x = x - ch;
 
        // Temporary String to generate one
        // plaintext at a time
        String curr = "";
 
        // Generate the probable ith plaintext
        // String using the shift calculated above
        for (int k = 0; k < N; k++) {
 
            // Insert whitespaces as it is
            if (S.charAt(k) == ' ') {
                curr += (char)' ';
                continue;
            }
 
            // Shift the kth letter of the
            // cipher by x
            int y = S.charAt(k) - 'A';
            y += x;
 
            if (y < 0)
                y += 26;
            if (y > 25)
                y -= 26;
 
            // Add the kth calculated/shifted
            // letter to temporary String
            curr += (char)('A' + y);
        }
 
        plaintext[i] = curr;
    }
 
    // Print the generated 5 possible plaintexts
    for (int i = 0; i < 5; i++) {
        System.out.print(plaintext[i] +"\n");
    }
}
static int[] reverse(int a[]) {
    int i, n = a.length, t;
    for (i = 0; i < n / 2; i++) {
        t = a[i];
        a[i] = a[n - i - 1];
        a[n - i - 1] = t;
    }
    return a;
}
// Driver Code
public static void main(String[] args)
{
    // Given String
    String S = "B TJNQMF NFTTBHF";
    int N = S.length();
 
    // Function Call
    printString(S, N);
 
}
}
 
// This code contributed by Princi Singh


Python3




# Python3 program for the above approach
 
# Function to decrypt a monoalphabetic
# substitution cipher using the letter
# frequency attack
def printString(S, N):
     
    # Stores final 5 possible deciphered
    # plaintext
    plaintext = [None] * 5
     
    # Store the frequency of each letter in
    # cipher text
    freq = [0] * 26
     
    # Stores the frequency of each letter
    # in cipher text in descending order
    freqSorted = [None] * 26
     
    # Store which alphabet is used already
    used = [0] * 26
     
    # Traverse the string S
    for i in range(N):
        if S[i] != ' ':
            freq[ord(S[i]) - 65] += 1
             
    # Copy the frequency array        
    for i in range(26):
        freqSorted[i] = freq[i]
         
    # Stores the string formed from
    # concatenating the english letters
    # in the decreasing frequency in the
    # english language    
    T = "ETAOINSHRDLCUMWFGYPBVKJXQZ"
     
    # Sort the array in descending order
    freqSorted.sort(reverse = True)
     
    # Iterate over the range [0, 5]
    for i in range(5):
        ch = -1
         
        # Iterate over the range [0, 26]
        for j in range(26):
            if freqSorted[i] == freq[j] and used[j] == 0:
                used[j] = 1
                ch = j
                break
             
        if ch == -1:
            break
         
        # Store the numerical equivalent of letter
        # at ith index of array letter_frequency
        x = ord(T[i]) - 65
         
        # Calculate the probable shift used
        # in monoalphabetic cipher
        x = x - ch
         
        # Temporary string to generate one
        # plaintext at a time
        curr = ""
         
        # Generate the probable ith plaintext
        # string using the shift calculated above
        for k in range(N):
             
            # Insert whitespaces as it is
            if S[k] == ' ':
                curr += " "
                continue
             
            # Shift the kth letter of the
            # cipher by x
            y = ord(S[k]) - 65
            y += x
             
            if y < 0:
                y += 26
            if y > 25:
                y -= 26
             
            # Add the kth calculated/shifted
            # letter to temporary string    
            curr += chr(y + 65)
             
        plaintext[i] = curr
     
    # Print the generated 5 possible plaintexts    
    for i in range(5):
        print(plaintext[i])
 
# Driver code
 
# Given string
S = "B TJNQMF NFTTBHF"
N = len(S)
 
# Function Call
printString(S, N)
 
# This code is contributed by Parth Manchanda


C#




// C# program for the above approach
using System;
 
public class GFG{
 
// Function to decrypt a monoalphabetic
// substitution cipher using the letter
// frequency attack
static void printString(String S, int N)
{
 
    // Stores readonly 5 possible deciphered
    // plaintext
    String []plaintext = new String[5];
 
    // Store the frequency of each letter in
    // cipher text
    int []freq = new int[26];
 
    // Stores the frequency of each letter
    // in cipher text in descending order
    int []freqSorted = new int[26];
 
    // Store which alphabet is used already
    int []Used = new int[26];
 
    // Traverse the String S
    for (int i = 0; i < N; i++) {
        if (S[i] != ' ') {
            freq[S[i] - 'A']++;
        }
    }
 
    // Copy the frequency array
    for (int i = 0; i < 26; i++) {
        freqSorted[i] = freq[i];
    }
 
    // Stores the String formed from concatenating
    // the english letters in the decreasing frequency
    // in the english language
    String T = "ETAOINSHRDLCUMWFGYPBVKJXQZ";
 
    // Sort the array in descending order
    Array.Sort(freqSorted);
    freqSorted= reverse(freqSorted);
    // Iterate over the range [0, 5]
    for (int i = 0; i < 5; i++) {
 
        int ch = -1;
 
        // Iterate over the range [0, 26]
        for (int j = 0; j < 26; j++) {
 
            if (freqSorted[i] == freq[j] && Used[j] == 0) {
                Used[j] = 1;
                ch = j;
                break;
            }
        }
        if (ch == -1)
            break;
 
        // Store the numerical equivalent of letter at
        // ith index of array letter_frequency
        int x = T[i] - 'A';
         
        // Calculate the probable shift used
        // in monoalphabetic cipher
        x = x - ch;
 
        // Temporary String to generate one
        // plaintext at a time
        String curr = "";
 
        // Generate the probable ith plaintext
        // String using the shift calculated above
        for (int k = 0; k < N; k++) {
 
            // Insert whitespaces as it is
            if (S[k] == ' ') {
                curr += (char)' ';
                continue;
            }
 
            // Shift the kth letter of the
            // cipher by x
            int y = S[k] - 'A';
            y += x;
 
            if (y < 0)
                y += 26;
            if (y > 25)
                y -= 26;
 
            // Add the kth calculated/shifted
            // letter to temporary String
            curr += (char)('A' + y);
        }
 
        plaintext[i] = curr;
    }
 
    // Print the generated 5 possible plaintexts
    for (int i = 0; i < 5; i++) {
        Console.Write(plaintext[i] +"\n");
    }
}
static int[] reverse(int []a) {
    int i, n = a.Length, t;
    for (i = 0; i < n / 2; i++) {
        t = a[i];
        a[i] = a[n - i - 1];
        a[n - i - 1] = t;
    }
    return a;
}
   
// Driver Code
public static void Main(String[] args)
{
    // Given String
    String S = "B TJNQMF NFTTBHF";
    int N = S.Length;
 
    // Function Call
    printString(S, N);
 
}
}
 
// This code is contributed by shikhasingrajput


Javascript




function printString(S) {
    // Stores final 5 possible deciphered plaintext
    var plaintext = new Array(5);
 
    // Store the frequency of each letter in cipher text
    var freq = new Array(26).fill(0);
 
    // Stores the frequency of each letter in cipher text in descending order
    var freqSorted = new Array(26);
 
    // Store which alphabet is used already
    var used = new Array(26).fill(0);
 
    // Traverse the string S
    for (var i = 0; i < S.length; i++) {
        if (S[i] !== ' ') {
            freq[S.charCodeAt(i) - 65] += 1;
        }
    }
 
    // Copy the frequency array
    for (var i = 0; i < 26; i++) {
        freqSorted[i] = freq[i];
    }
 
    // Stores the string formed from concatenating the english letters in the decreasing frequency in the english language
    var T = "ETAOINSHRDLCUMWFGYPBVKJXQZ";
 
    // Sort the array in descending order
    freqSorted.sort(function(a, b) { return b - a });
 
    // Iterate over the range [0, 5]
    for (var i = 0; i < 5; i++) {
        var ch = -1;
 
        // Iterate over the range [0, 26]
        for (var j = 0; j < 26; j++) {
            if (freqSorted[i] === freq[j] && used[j] === 0) {
                used[j] = 1;
                ch = j;
                break;
            }
        }
 
        if (ch === -1) {
            break;
        }
 
        // Store the numerical equivalent of letter
        // at ith index of array letter_frequency
        var x = T.charCodeAt(i) - 65;
 
        // Calculate the probable shift used in monoalphabetic cipher
        x = x - ch;
 
        // Temporary string to generate one plaintext at a time
        var curr = "";
 
        // Generate the probable ith plaintext string using the shift calculated above
        for (var k = 0; k < S.length; k++) {
 
            // Insert whitespaces as it is
            if (S[k] === ' ') {
                curr += " ";
                continue;
            }
 
            // Shift the kth letter of the cipher by x
            var y = S.charCodeAt(k) - 65;
            y += x;
 
            if (y < 0) {
                y += 26;
            }
            if (y > 25) {
                y -= 26;
            }
 
            // Add the kth calculated/shifted letter to temporary string
            curr += String.fromCharCode(y + 65);
        }
        plaintext[i] = curr;
    }
 
    console.log(plaintext);
}
 
// This Code is Contributed By Shivam Tiwari


Output

A SIMPLE MESSAGE
B TJNQMF NFTTBHF
A SIMPLE MESSAGE
C UKORNG OGUUCIG
C UKORNG OGUUCIG

Time Complexity: O(N)
Auxiliary Space: O(N)



Last Updated : 30 Jan, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads