I'll start by explaining what a cryptarithmetic problem is, through an example:
T W O
+ T W O
F O U R
We have to assign a digit [0-9] to each letter such that no two letters share the same digit and it satisfies the above equation.
One solution to the above problem is:
7 6 5
+ 7 6 5
1 5 3 0
There are two ways to solve this problem, one is brute force, this will work but it's not the optimal way. The other way is using constraint satisfaction.
Solution using Constraint Satisfaction
We know that R will always be even because its 2 * O
this narrows down O's domain to {0, 2, 4, 6, 8}
We also know that F can't be anything but 1, since F isn't an addition of two letters, it must be getting its value from carry generated by T + T = O
This also implies that T + T > 9, only then will it be able to generate a carry for F;
This tells us that T > 4 {5, 6, 7, 8, 9}
And as we go on doing this, we keep on narrowing down the domain and this helps us reduce time complexity by a considerable amount.
The concept seems easy, but I'm having trouble implementing it in C++. Especially the part where we generate constraints/domain for each variable. Keep in mind that there are carries involved too.
EDIT: I'm looking for a way to generate a domain for each variable using the concept I stated.
This kind of problem is a good application for generic constraint programming packages like Google's open source OR-Tools. (See https://developers.google.com/optimization and https://developers.google.com/optimization/cp/cryptarithmetic).
The package is written in c++, so it should be a good match for you.
Then programming the problem is as simple as this (sorry, since I work with OR-Tools in c#, this is c# code, but the c++ code will look pretty much the same)
public void initModel(CpModel model)
{
// Make variables
T = model.NewIntVar(0, 9, "T");
W = model.NewIntVar(0, 9, "W");
O = model.NewIntVar(0, 9, "O");
F = model.NewIntVar(0, 9, "F");
U = model.NewIntVar(0, 9, "U");
R = model.NewIntVar(0, 9, "R");
// Constrain the sum
model.Add((2 * (100 * T + 10 * W + O)) == (1000 * F + 100 * O + 10 * U + R));
// Make sure the variables are all different
model.AddAllDifferent(decisionVariables);
// The leading digit shouldn't be 0
model.Add(T != 0);
model.Add(F != 0);
}
and then calling the Solve method.
In the constraint for the sum, the operators* + and == are all overridden in the package to create objects that can be used by the model to enforce the constraint.
This is the start of the output which enumerates the solution
Solution #0: time = 0,00 s;
T = 8
W = 6
O = 7
F = 1
U = 3
R = 4
Solution #1: time = 0,01 s;
T = 8
W = 4
O = 6
F = 1
U = 9
R = 2
Solution #2: time = 0,01 s;
T = 8
W = 3
O = 6
F = 1
U = 7
R = 2
Solution #3: time = 0,01 s;
T = 9
W = 3
O = 8
F = 1
U = 7
R = 6
And here's the complete code including solution printing and Main method for the execution:
using Google.OrTools.Sat;
using System;
using System.IO;
namespace SO69626335_CryptarithmicPuzzle
{
class Program
{
static void Main(string[] args)
{
try
{
Google.OrTools.Sat.CpModel model = new CpModel();
ORModel myModel = new ORModel();
myModel.initModel(model);
IntVar[] decisionVariables = myModel.decisionVariables;
// Creates a solver and solves the model.
CpSolver solver = new CpSolver();
VarArraySolutionPrinter solutionPrinter = new VarArraySolutionPrinter(myModel.variablesToPrintOut);
solver.SearchAllSolutions(model, solutionPrinter);
Console.WriteLine(String.Format("Number of solutions found: {0}",
solutionPrinter.SolutionCount()));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
throw;
}
Console.WriteLine("OK");
Console.ReadKey();
}
}
class ORModel
{
IntVar T;
IntVar W;
IntVar O;
IntVar F;
IntVar U;
IntVar R;
public void initModel(CpModel model)
{
// Make variables
T = model.NewIntVar(0, 9, "T");
W = model.NewIntVar(0, 9, "W");
O = model.NewIntVar(0, 9, "O");
F = model.NewIntVar(0, 9, "F");
U = model.NewIntVar(0, 9, "U");
R = model.NewIntVar(0, 9, "R");
// Constrain the sum
model.Add((2 * (100 * T + 10 * W + O)) == (1000 * F + 100 * O + 10 * U + R));
// Make sure the variables are all different
model.AddAllDifferent(decisionVariables);
// The leading digit shouldn't be 0
model.Add(T != 0);
model.Add(F != 0);
}
public IntVar[] decisionVariables
{
get
{
return new IntVar[] { T, W, O, F, U, R };
}
}
public IntVar[] variablesToPrintOut
{
get
{
return decisionVariables;
}
}
}
public class VarArraySolutionPrinter : CpSolverSolutionCallback
{
private int solution_count_;
private IntVar[] variables;
public VarArraySolutionPrinter(IntVar[] variables)
{
this.variables = variables;
}
public override void OnSolutionCallback()
{
// using (StreamWriter sw = new StreamWriter(#"C:\temp\GoogleSATSolverExperiments.txt", true, Encoding.UTF8))
using (TextWriter sw = Console.Out)
{
sw.WriteLine(String.Format("Solution #{0}: time = {1:F2} s;",
solution_count_, WallTime()));
foreach (IntVar v in variables)
{
sw.Write(
String.Format(" {0} = {1}\r\n", v.ShortString(), Value(v)));
}
solution_count_++;
sw.WriteLine();
}
if (solution_count_ >= 10)
{
StopSearch();
}
}
public int SolutionCount()
{
return solution_count_;
}
}
}
A full solution is way out of scope for a simple SO question, but I can sketch what you would need.
First, generate new letters for the carries:
0 T W O
0 T W O
+ Z Y X V
F O U R
You can then generate a std::map<char, std::set<int>> containing all the options. The letters have the standard range {0..9}, V is {0}, Z is {1} and Y and X have {0..1}.
Next, you need to encode the additions into a set of clauses.
enum class Op { Equal, SumMod10, SumDiv10, Even, Odd };
struct clause { Op op; std::vector<Var> children; };
std::vector<clause> clauses{
{Equal, { 'Z' , 'F'}},
{SumMod10, {'O', 'T', 'T', 'Y'}}, // O = (T+T+Y) mod 10
{SumMod10, {'U', 'W', 'W', 'X'}},
{SumMod10, {'R', 'O', 'O', 'V'}},
{SumDiv10, {'F', 'T', 'T', 'Y'}}, // F is the carry of T+T+Y
{SumDiv10, {'O', 'W', 'W', 'X'}},
{SumDiv10, {'U', 'O', 'O', 'V'}},
};
Then the fun part begins: you need to create a calculation that will try to simplify the constraints using the knowledge it has.
For example, {SumMod10, {'U', 'O', 'O', 'V'}} can be simplified to {SumMod10, {'U', 'O', 'O', 0}} since V=0.
Sometimes a clause can reduce the range of a variable, for example the {Equal, {'Z', 'F'}} constraint can immediately reduce the range of F to {0,1}.
Next, you need to teach your system about basic algebraic equalities for furhter simplification, such as:
{SumMod10, {A, 0, C}} === {SumMod10, {A, C, 0}} === {Equal, {A,C}}
and even more abstract things like "if A >= 5 and B >= 5 then A+B >= 10" or "if A is even and B is even then A + B is also even".
Finally, your system needs to be able to assume hypotheses and disprove them, or prove that a hypothesis is true no matter what, like you did in your post.
For example, assuming that R is odd would mean that O + O is odd, which can only happen if O is odd and even at the same time. Therefore R must be even.
At the end of the day, you will have implemented not only a formal system for describing and evaluating boolean clauses in the numbers domain, you will also have a goal-driven solution engine to go with it. If this is more than just an idle musing, I would strongly look into adoption an SMT system to solve this for you or at least learning Prolog and expressing your problem there.
Here is how I solved it using backtracking
My approach here was to smartly brute force it, I recursively assign every possible value [0-9] to each letter and check if there is any contradiction.
Contradictions can be one of the following:
Two or more letters end up having the same value.
Sum of letters don't match the value of the result letter.
Sum of letters is already assigned to some letter.
As soon as a contradiction occurs, the recursion for that particular combination ends.
#include <bits/stdc++.h>
using namespace std;
vector<string> words, wordOg;
string result, resultOg;
bool solExists = false;
void reverse(string &str){
reverse(str.begin(), str.end());
}
void printProblem(){
cout<<"\n";
for(int i=0;i<words.size();i++){
for(int j=0;j<words[i].size();j++){
cout<<words[i][j];
}
cout<<"\n";
}
cout<<"---------\n";
for(int i=0;i<result.size();i++){
cout<<result[i];
}
cout<<"\n";
}
void printSolution(unordered_map<char, int> charValue){
cout<<"\n";
for(int i=0;i<words.size();i++){
for(int j=0;j<words[i].size();j++){
cout<<charValue[wordOg[i][j]];
}
cout<<"\n";
}
cout<<"---------\n";
for(int i=0;i<result.size();i++){
cout<<charValue[resultOg[i]];
}
cout<<"\n";
}
void solve(int colIdx, int idx, int carry, int sum,unordered_map<char, int> charValue, vector<int> domain){
if(colIdx<words.size()){
if(idx<words[colIdx].size()){
char ch = words[colIdx][idx];
if(charValue.find(ch)!=charValue.end()){
solve(colIdx + 1, idx, carry, sum + charValue[ch], charValue, domain);
}
else{
for(int i=0;i<10;i++){
if(i==0 && idx==words[colIdx].size()-1) continue;
if(domain[i]==-1){
domain[i] = 0;
charValue[ch] = i;
solve(colIdx + 1, idx, carry, sum + i, charValue, domain);
domain[i] = -1;
}
}
}
}
else solve(colIdx + 1, idx, carry, sum, charValue, domain);
}
else{
if(charValue.find(result[idx])!=charValue.end()){
if(((sum+carry)%10)!=charValue[result[idx]]) return;
}
else{
if(domain[(sum + carry)%10]!=-1) return;
domain[(sum + carry)%10] = 0;
charValue[result[idx]] = (sum + carry)%10;
}
carry = (sum+carry)/10;
if(idx==result.size()-1 && (charValue[result[idx]]==0 || carry == 1)) return;
if(idx+1<result.size()) solve(0, idx+1, carry, 0, charValue, domain);
else{
solExists = true;
printSolution(charValue);
}
}
}
int main() {
unordered_map<char, int> charValue;
vector<int> domain(10,-1);
int n;
cout<<"\nEnter number of input words: ";
cin>>n;
cout<<"\nEnter the words: ";
for(int i=0;i<n;i++){
string inp;
cin>>inp;
words.push_back(inp);
}
cout<<"\nEnter the resultant word: ";
cin>>result;
printProblem();
wordOg = words;
resultOg = result;
reverse(result);
for(auto &itr: words) reverse(itr);
solve(0, 0, 0, 0, charValue, domain);
if(!solExists) cout<<"\nNo Solution Exists!";
return 0;
}
Related
I am writing a program that encrypts/decrypts a message using Caesar Cipher and a shift number given by the user. I got that part down, more or less.
However, the final part of the program performs a brute force attack on an encrypted message, and we are attempting to find the shift key that was used to encrypt it. I am having trouble determining the best way to stop the program when it finds the match.
I will paste my code below, and what it comes up with running it now. I have tested with pencil and paper, and can see the key is 17 for our particular input, so I put a crap condition just to get it working. I am wondering how to stop it when it finds the correct value and/or a sentence that is actual English, so that it works for every input, not just our test case.
P.S. I apologize if this is answered elsewhere, the only posts I found on the topic were more related to stopping brute force attacks and/or having secure passwords.
char decrypt(char letter, int key);
int main(){
// Part 3 Brute-Force Attack
std::string ciphertext;
std::cout << "Enter in the ciphertext: ";
std::getline(std::cin, ciphertext);
std::cout << "\nBrute-Force Decryptions:" << std::endl;
int i = 0;
int keyCode = 0;
std::string gang = "";
int alphaArray[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
// The algorithm has nested for each loops, the first one keeps track of the key initialized to 0
// and the second loop goes through the string given and converts it to the proper value based
// on the shift key. The reason we want to find 't', 'h' or 'e' is because this signifies the
// begginning of an actual English sentence, and most likely our cracked message. I was able to
// determine this would begin the correct string we are looking for by bruteforcing the first // few letters using pencil and paper
for(auto key : alphaArray){
std::cout << "\nkey = " << key << ":\t";
for(auto letter : ciphertext){
letter = decrypt(letter, i);
gang += letter;
}
std::cout << gang << std::endl;
if (tolower(ciphertext[0]) - key + 26 == 't' && tolower(ciphertext[1]) - key == 'h' && tolower(ciphertext[2]) - key == 'e'){
keyCode = key;
break;
}
i++;
gang = "";
}
// Output results
std::cout << "\nPart 3 KeyCode: " << keyCode << std::endl;
std::cout << "\n\n";
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////
// Function: Attempts to decrypt letter by shifting it up the alphabet 'key' times /
// Params: Letter as a char and key as an int value /
// Return: Letter shifted 'key' times /
//////////////////////////////////////////////////////////////////////////////////////
char decrypt(char letter, int key){
int position = (letter - key - 'A') % 26;
if(position < 0)
position += 26;
return (char)position + 'a';
}
Output:
Brute-Force Decryptions:
key = 0: kyvtjyzwkttzgyvitzjtrcjftbefnetrjtkyvttrvjrittzgyviftrwkvitalczljttrvjriftnyftivglkvucptljvutkyvttzgyvitkftgrjjtdvjjrxvjtkftyzjtkiffgjtulizextyzjtdzczkripttrdgrzxejtzetxrlchtefkvtkyrktkyvttrvjrittzgyvitzjtvrjzcpttirtbvutsptkyvtsilkvgwfitvtrkkrtbh
key = 1: jxusixyvjssyfxuhsyisqbiesademdsqisjxussquiqhssyfxuhesqvjuhszkbykissquiqhesmxeshufkjutboskiutsjxussyfxuhsjesfqiiscuiiqwuisjesxyisjheefistkhydwsxyiscybyjqhossqcfqywdisydswqkbgsdejusjxqjsjxussquiqhssyfxuhsyisuqiybosshqsautsrosjxusrhkjufvehsusqjjqsag
key = 2: iwtrhwxuirrxewtgrxhrpahdrzcdlcrphriwtrrpthpgrrxewtgdrpuitgryjaxjhrrpthpgdrlwdrgtejitsanrjhtsriwtrrxewtgridrephhrbthhpvthridrwxhrigddehrsjgxcvrwxhrbxaxipgnrrpbepxvchrxcrvpjafrcditriwpiriwtrrpthpgrrxewtgrxhrtphxanrrgprztsrqnriwtrqgjiteudgrtrpiiprzf
key = 3: hvsqgvwthqqwdvsfqwgqozgcqybckbqogqhvsqqosgofqqwdvsfcqothsfqxizwigqqosgofcqkvcqfsdihsrzmqigsrqhvsqqwdvsfqhcqdoggqasggousgqhcqvwgqhfccdgqrifwbuqvwgqawzwhofmqqoadowubgqwbquoizeqbchsqhvohqhvsqqosgofqqwdvsfqwgqsogwzmqqfoqysrqpmqhvsqpfihsdtcfqsqohhoqye
key = 4: gurpfuvsgppvcurepvfpnyfbpxabjapnfpgurppnrfneppvcurebpnsgrepwhyvhfppnrfnebpjubperchgrqylphfrqpgurppvcurepgbpcnffpzrffntrfpgbpuvfpgebbcfpqhevatpuvfpzvyvgnelppnzcnvtafpvaptnhydpabgrpgungpgurppnrfneppvcurepvfprnfvylppenpxrqpolpgurpoehgrcsbeprpnggnpxd
key = 5: ftqoeturfooubtqdoueomxeaowzaizomeoftqoomqemdooubtqdaomrfqdovgxugeoomqemdaoitaodqbgfqpxkogeqpoftqooubtqdofaobmeeoyqeemsqeofaotueofdaabeopgduzsotueoyuxufmdkoomybmuszeouzosmgxcozafqoftmfoftqoomqemdooubtqdoueoqmeuxkoodmowqponkoftqondgfqbradoqomffmowc
key = 6: espndstqenntaspcntdnlwdznvyzhynldnespnnlpdlcnntaspcznlqepcnufwtfdnnlpdlcznhszncpafepowjnfdponespnntaspcneznalddnxpddlrpdneznstdneczzadnofctyrnstdnxtwtelcjnnlxaltrydntynrlfwbnyzepneslenespnnlpdlcnntaspcntdnpldtwjnnclnvponmjnespnmcfepaqzcnpnleelnvb
key = 7: dromcrspdmmszrobmscmkvcymuxygxmkcmdrommkockbmmszrobymkpdobmtevsecmmkockbymgrymbozedonvimeconmdrommszrobmdymzkccmwocckqocmdymrscmdbyyzcmnebsxqmrscmwsvsdkbimmkwzksqxcmsxmqkevamxydomdrkdmdrommkockbmmszrobmscmokcsvimmbkmuonmlimdromlbedozpybmomkddkmua
key = 8: cqnlbqrocllryqnalrbljubxltwxfwljblcqnlljnbjallryqnaxljocnalsdurdblljnbjaxlfqxlanydcnmuhldbnmlcqnllryqnalcxlyjbblvnbbjpnblcxlqrblcaxxyblmdarwplqrblvrurcjahlljvyjrpwblrwlpjduzlwxcnlcqjclcqnlljnbjallryqnalrblnjbruhllajltnmlkhlcqnlkadcnyoxalnljccjltz
key = 9: bpmkapqnbkkqxpmzkqakitawksvwevkiakbpmkkimaizkkqxpmzwkinbmzkrctqcakkimaizwkepwkzmxcbmltgkcamlkbpmkkqxpmzkbwkxiaakumaaiomakbwkpqakbzwwxaklczqvokpqakuqtqbizgkkiuxiqovakqvkoictykvwbmkbpibkbpmkkimaizkkqxpmzkqakmiaqtgkkziksmlkjgkbpmkjzcbmxnwzkmkibbiksy
key = 10: aoljzopmajjpwolyjpzjhszvjruvdujhzjaoljjhlzhyjjpwolyvjhmalyjqbspbzjjhlzhyvjdovjylwbalksfjbzlkjaoljjpwolyjavjwhzzjtlzzhnlzjavjopzjayvvwzjkbypunjopzjtpspahyfjjhtwhpnuzjpujnhbsxjuvaljaohajaoljjhlzhyjjpwolyjpzjlhzpsfjjyhjrlkjifjaoljiybalwmvyjljhaahjrx
key = 11: znkiynolziiovnkxioyigryuiqtuctigyiznkiigkygxiiovnkxuiglzkxiparoayiigkygxuicnuixkvazkjreiaykjiznkiiovnkxizuivgyyiskyygmkyizuinoyizxuuvyijaxotminoyisorozgxeiigsvgomtyiotimgarwituzkizngziznkiigkygxiiovnkxioyikgyoreiixgiqkjiheiznkihxazkvluxikigzzgiqw
key = 12: ymjhxmnkyhhnumjwhnxhfqxthpstbshfxhymjhhfjxfwhhnumjwthfkyjwhozqnzxhhfjxfwthbmthwjuzyjiqdhzxjihymjhhnumjwhythufxxhrjxxfljxhythmnxhywttuxhizwnslhmnxhrnqnyfwdhhfrufnlsxhnshlfzqvhstyjhymfyhymjhhfjxfwhhnumjwhnxhjfxnqdhhwfhpjihgdhymjhgwzyjuktwhjhfyyfhpv
key = 13: xligwlmjxggmtlivgmwgepwsgorsargewgxliggeiwevggmtlivsgejxivgnypmywggeiwevsgalsgvityxihpcgywihgxliggmtlivgxsgtewwgqiwwekiwgxsglmwgxvsstwghyvmrkglmwgqmpmxevcggeqtemkrwgmrgkeypugrsxigxlexgxliggeiwevggmtlivgmwgiewmpcggvegoihgfcgxligfvyxitjsvgigexxegou
key = 14: wkhfvkliwfflskhuflvfdovrfnqrzqfdvfwkhffdhvdufflskhurfdiwhufmxolxvffdhvdurfzkrfuhsxwhgobfxvhgfwkhfflskhufwrfsdvvfphvvdjhvfwrfklvfwurrsvfgxulqjfklvfplolwdubffdpsdljqvflqfjdxotfqrwhfwkdwfwkhffdhvdufflskhuflvfhdvlobffudfnhgfebfwkhfeuxwhsirufhfdwwdfnt
key = 15: vjgeujkhveekrjgtekuecnuqempqypecuevjgeecgucteekrjgtqechvgtelwnkwueecguctqeyjqetgrwvgfnaewugfevjgeekrjgtevqercuueoguuciguevqejkuevtqqruefwtkpiejkueoknkvctaeecorckipuekpeicwnsepqvgevjcvevjgeecgucteekrjgtekuegcuknaeetcemgfedaevjgedtwvgrhqtegecvvcems
key = 16: uifdtijguddjqifsdjtdbmtpdlopxodbtduifddbftbsddjqifspdbgufsdkvmjvtddbftbspdxipdsfqvufemzdvtfeduifddjqifsdupdqbttdnfttbhftdupdijtdusppqtdevsjohdijtdnjmjubszddbnqbjhotdjodhbvmrdopufduibuduifddbftbsddjqifsdjtdfbtjmzddsbdlfedczduifdcsvufqgpsdfdbuubdlr
key = 17: thecshiftccipherciscalsocknowncasctheccaesarccipherocaftercjuliusccaesarocwhocreputedlycusedctheccipherctocpasscmessagesctochisctroopscduringchiscmilitaryccampaignscincgaulqcnotecthatctheccaesarcciphercisceasilyccrackedcbycthecbrutepforcecattackq
Part 3 KeyCode: 17
Brute-force-breaking Caesar-Cypher can be easily done by using heuristics. A very good and simple approach is, to use the average frequency of a letter in a given language. Such tables can be found on the internet for many languages.
Let us look at the English language. I found for example the following:
a: 0.0684
b: 0.0139
c: 0.0146
d: 0.0456
e: 0.1267
f: 0.0234
g: 0.0180
h: 0.0701
i: 0.0640
j: 0.0033
k: 0.0093
l: 0.0450
m: 0.0305
n: 0.0631
o: 0.0852
p: 0.0136
q: 0.0004
r: 0.0534
s: 0.0659
t: 0.0850
u: 0.0325
v: 0.0084
w: 0.0271
x: 0.0007
y: 0.0315
z: 0.0004
Later we need to look up this occurrence value for each letter of the alphabet. We will use lowercase only. We can define a compile-time array for that.
constexpr std::array<double, 26> LetterWeight{ .0684,.0139,.0146,.0456,.1267,
.0234,.0180,.0701,.0640,.0033,.0093,.0450,.0305,.0631,.0852,.0136,.0004,.0534,
.0659,.0850,.0325,.0084,.0271,.0007,.0315,.0004 };
Then, we will do the following. With the brute force approach, we will try all possible keys for decryption in a loop.
So, we will decrypt the encrypted message 25 times. We can omit the key 0, because then the message would be clear and readable.
In each loop for the 25 keys, we will additionally analyze each letter of the decrypted message (if it is the right key or not does not matter at this moment).
We will build a score for this decrypted string by adding up the above defined occurrence-value for each key based on the letter.
In the end, we will have a sum for each key / letter of the decrypted string. Additionally, we will store the key that has been used for those sums.
For this, we will define a std::array of std::pair. There will be 26 elements in the std::array. One for each of the tested keys. The content will be the sum of letter-occurrence-values for each key and again the key.
std::array<std::pair<double, int>, 26> score{};
We need to store again the key in the array, because we want to sort the std::array later to find the highest score and its corresponding key.
Regarding the encryption/decryption function, I gave a detailed answer with a long explanation here. Please, kindly check this.
Now, with all the above wisdom, we can come up with the following code cracker:
#include <iostream>
#include <array>
#include <cctype>
#include <string>
#include <algorithm>
// Some test string
const std::string test{ R"(Kyv rcxfizkyd yrj evjkvu wfi vrty cffgj, kyv wzijk fev bvvgj kirtb fw kyv bvp zezkzrczqvu kf 0 reu kyv
jvtfeu cffg xfvj kyiflxy kyv jkizex xzmve reu tfemvikj zk kf kyv gifgvi mrclv srjvu fe kyv jyzwk bvp.
Kyv ivrjfe nv nrek kf wzeu 'k', 'y' fi 'v' zj svtrljv kyzj jzxezwzvj kyv svxxzeezex fw re rtklrc Vexczjy
jvekvetv,reu dfjk czbvcp fli tirtbvu dvjjrxv.Z nrj rscv kf uvkvidzev kyzj nflcu svxze kyv tfiivtk jkizex
nv riv cffbzex wfi sp silkvwfitzex kyv wzijk wvn cvkkvij ljzex gvetzc reu grgvi.)" };
// Letter frequencies in the English Language
constexpr std::array<double, 26> LetterWeight{ .0684,.0139,.0146,.0456,.1267,.0234,.0180,.0701,.0640,.0033,.0093,.0450,.0305,.0631,.0852,.0136,.0004,.0534,.0659,.0850,.0325,.0084,.0271,.0007,.0315,.0004 };
// Simple function for Caesar encyption/decyption (decryption by simply using a negative key)
std::string caesar(const std::string& in, int key) {
std::string res(in.size(), ' ');
std::transform(in.begin(), in.end(), res.begin(), [&](char c) {return std::isalpha(c) ? (char)((((c & 31) - 1 + ((26 + (key % 26)) % 26)) % 26 + 65) | c & 32) : c; });
return res;
}
// Test code
int main() {
// We will try all possible ciphers 1..25
std::array<std::pair<double, int>, 26> score{};
for (int key = 1; key < 26; ++key) {
// Get one possible deciphered test
for (const char c : caesar(test, key)) {
// Calculate score according toLetter weight
score[key].first += (std::isalpha(c)) ? LetterWeight[(c & 31) - 1] : 0.0; // Build the score for this key, using the accumulated letter weight
score[key].second = key; // And store the key. Is necessary, becuase we will sort later and do not want to loose the key information
}
}
// Now sort for getting the index with the highes score
std::sort(score.begin(), score.end());
// And show the most probable result to the user.
std::cout << "Decrypted with key: "<< score.back().second-26 << "\n\n" << caesar(test, score.back().second) << "\n\n";
};
All the code breaking can be done with ~15 lines of code. So, rather simple.
The resulting output will be:
Minimum Deletions to Make String Balanced - LeetCode
1653. Minimum Deletions to Make String Balanced
Medium
You are given a string s consisting only of characters 'a' and 'b'.
You can delete any number of characters in s to make s balanced. s is balanced if there is no pair of indices (i,j) such that i < j and s[i] = 'b' and s[j]= 'a'.
Return the minimum number of deletions needed to make s balanced.
Example 1:
Input: s = "aababbab"
Output: 2
Explanation: You can either:
Delete the characters at 0-indexed positions 2 and 6 ("aababbab" -> "aaabbb"), or
Delete the characters at 0-indexed positions 3 and 6 ("aababbab" -> "aabbbb").
Example 2:
Input: s = "bbaaaaabb"
Output: 2
Explanation: The only solution is to delete the first two characters.
Constraints:
1 <= s.length <= 105
s[i] is 'a' or 'b'.
class Solution {
public:
int minimumDeletions(string s) {
int cnt=0;
stack<int> st;
st.push(s[0]);
for(int i=1;i<s.length();i++){
if(s[i]=='b'){
st.push(s[i]);
}
else{
if(st.empty()==true){
st.push('a');
}
else if(st.top()=='a'){
st.push('a');
}
else{
while(st.empty()==false and st.top()=='b'){
cnt++;
st.pop();
}
st.push('a');
}
}
}
return cnt;
}
};
for both the exapmles the answer should be 2,2
but my code it giving 3,2
I recommend to learn some test framework. IMO two best are:
gtest - industry standard - old but quite powerful
catch2 - nice and more user friendly, quickly gains popularity.
Write test code which will validate your solution then use it with a debugger to find issue in your code.
Here is an example with catch2:
https://godbolt.org/z/xevvPrEeK
TEST_CASE("Sum") {
auto [result, s] = GENERATE(table<int, std::string>({
{ 0, ""},
{ 0, "a"},
{ 0, "b"},
{ 0, "ab"},
{ 1, "ba"},
{ 1, "bba"},
{ 1, "baa"},
{ 1, "aba"},
{ 2, "aababbab" },
{ 2, "bbaaaaabb" },
}));
INFO("s = " << s);
REQUIRE(result == Solution{}.minimumDeletions(s));
}
Note this test finds simplest case where your code fails.
On problem: use of the stack is obsolete. Just try find place where you have to delete all bs before it and all as after it.
Problem Statement:
Given an equation “x=y”, for example, “111=12”, you need to add pluses
inside x to make the equation correct. In our example “111=12”, we can
add one plus “11+1=12” and the equation becomes correct. You need to
find the minimum number of pluses to add to x to make the equation
correct. If there is no answer print -1.
Note that the value of y won’t exceed 5000. The numbers in the
corrected equation may contain arbitrary amounts of leading zeros.
Input Format The first line contains a string, A as described in the
problem statement.
Constraints 1 <= len(A) <= 10^3
I tried the recursive approach. Which is for every character in the 'x', I have two options I can include the plus sign next to the current digit or move to the next digit (by including the current digit with the next digit) I checked all the combinations and found the minimum pluses. As you know, this is exponential in complexity. I'm not able to apply dynamic programming for this problem as I can't think of the states for the dynamic programming.
I know this problem can be solved by dynamic programming. But, I don't know how to identify the state and the transition.
The first thing that comes to mind is to have a table
int f[N+1][M+1];
where N = len(x) and M = y. Then f[i][j] would record the solution to the sub-problem substr(x,0,i)=j; i.e. how many pluses are needed to get the sum j from the first i digits of x. The table can be incrementally updated through the recurrence relation:
f[i][j] = minimum over 0 <= k < i of (f[k][j - atoi(substr(x,k,i))] + 1)
Configurations that aren't obtainable or out-of-bounds should be understood as having f[i][j] == +infinity rather than -1.
The size of the table will be O(N*M) and the running time is O(N² M).
I'll leave the implementation details and the starting condition for you to complete.
Backtracking along with DP (Memoization) helped me to pass all the cases
here is my code. It passed all the cases in the given time limit
all_ans = {}
def min_pulses(A, target):
if (A, target) in all_ans:
return all_ans[(A, target)]
if len(A) == 0:
if target != 0:
return -1
else:
return 0
while len(A) > 0 and A[0] == '0':
A = A[1:]
if len(A) == 0 and target == 0:
return 1
if target < 0:
return -1
i = 1
ans = float('inf')# initializing ans to infinite number so that min can be update
while i <= 5 and i <=len(A):
curr_num = A[:i]
curr_ans = min_pulses(A[i:], target - int(curr_num))
if curr_ans >= 0:
ans = min(1 + curr_ans, ans)
i += 1
if ans == float('inf'):
ans = -1
all_ans[(A,target)] = ans
return ans
equation = input().split('=')
A = equation[0]
target = int(equation[1])
groups = min_pulses(A, target)
if groups < 0:
print(-1)
else:
print(groups - 1)
//import java.io.*;
import java.util.*;
//import java.lang.Math;
class NewClass15{
public static int minimum_pluses(String S)
{
StringBuilder s = new StringBuilder();
int target=0;
for(int i=0;i<S.length();i++) //distinguishing left and right strings respectively
{
if(S.charAt(i)=='=')
{
target=Integer.parseInt(S.substring(i+1,S.length()));
break;
}
s.append(S.charAt(i));
}
dp = new int[1000][5001][6];
int temp = dfs(s.toString(),0,0,target);
if(temp>=max)
return -1;
else
return temp;
}
static int dp[][][];
private static int dfs(String s,int len,int ind,int target)
{
if(target<0||len>5) return max;
if(ind==s.length())
{
int x=0;
if(len!=0)
x=Integer.parseInt(s.substring(ind-len,ind));
target-=x;
if(target==0) return 0;
return max;
}
if(dp[ind][target][len]!=0)
{
System.out.println("1 dfs("+ind+","+target+","+len+")");
return dp[ind][target][len]-1;
}
//add
long ans=max;
if(s.charAt(ind)=='0' && len==0)
{
System.out.println("2 dfs("+0+","+(ind+1)+","+target+")");
ans=Math.min(ans,dfs(s,0,ind+1,target));
return (int)(ans);
}
System.out.println("3 dfs("+(len+1)+","+(ind+1)+","+target+")");
ans=Math.min(ans,dfs(s,len+1,ind+1,target));
//add +
if(len!=0)
{
int x=Integer.parseInt(s.substring(ind-len,ind));
int j=ind;
while(j<s.length() && s.charAt(j)=='0') j++;
if(j!=s.length()) j=j+1;
System.out.println("4 dfs("+(1)+","+(j)+","+(target-x)+")");
ans=Math.min(ans,1+dfs(s,1,j,target-x));
}
System.out.println("final dfs("+ind+","+target+","+len+")");
dp[ind][target][len]=(int)(ans+1);
return (int)(ans);
}
static int max=10000;
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
String A;
A=scan.next();
int result;
result = minimum_pluses(A);
System.out.print(result);
}
}
This is the answer in java if it is of some help. I have not written the code though.
Can you provide me with some testcases for the given Minimum Pluses Question?
Thank you.
"""2nd one Answer"""
def permute(s):
result = [[s]]
for i in range(1, len(s)):
first = [s[:i]]
rest = s[i:]
for p in permute(rest):
result.append(first + p)
return [[int(j) for j in i] for i in result]
def problem(s):
x,y=s.split("=")
data=permute(x)
newdata=[]
for i in range(1,len(x)+1,1):
for j in data:
if i==len(j):
newdata.append(j)
for i in newdata:
if sum(i)==int(y):
print("str 1",i)
return
print("str -1")
def check_constraint(s):
if (not (1<=len(s)<=10^3)):
print(-1)
elif (s.split("=")[0]==s.split("=")[1]):
print(1)
elif (not (len(s.split("=")[0])>=len(s.split("=")[1]))):
print(-1)
else:
problem(s)
A=input()
check_constraint(A)
So I need to make a program that lists all permutations.
There are 4 characters:
"1",
"2",
"R",
"T"
The conditions is that the "R" needs to have "1" before and after him so it sits like this 1-R-1
The "T" condition is that either "1" or "2" are after him so it sits like this T-1 or T-2
The max length should be 10
The output should be like this:
111
112
121
122
1R1
1T1
1T2
211
212
221
222
2T1
2T2
T11
T12
T21
T22
I have managed to figure out the permutations part but I just cannot make them work with the conditions
void displayPermutation(string permutation[], int length){
int i;
for (i=0;i<length;i++){
cout<<permutation[i];
}
cout << endl;
}
void getPermutations(string operatorBank[], int operatorCount,
string permutation[],int permutationLength, int curIndex){
int i;
//stop recursion condition
if(curIndex == permutationLength){
displayPermutation(permutation,permutationLength);
}
else{
for(i = 0; i < operatorCount; i++){
permutation[curIndex] = operatorBank[i];
getPermutations(operatorBank,operatorCount,permutation,
permutationLength,curIndex+1);
}
}
}
int main ()
{
int operatorCount = 4;
int permutationLength = 3;
string operatorBank[] = {"1","2","R","T"};
string permutation[] = {"","","",""}; //empty string
int curIndex = 0;
getPermutations(operatorBank,operatorCount,permutation,
permutationLength,curIndex);
return 0;
}
You got your terms a little mixed up. You're not talking about permutations[1] but about combinations[2].
As far as I can tell you already have the algorithm (recursive backtracking) you're just not checking if your solution is valid, by filtering the solution space. So you're generating all solutions without taking into account any constraint and you print a solution when you reached the permutationLength. At this step you can also check if the solution is valid by checking if it abides by the conditions. If it is you print it, if not you discard it.
Strategy for this would be:
Look for R and check if permutation[idx-1] is 1 and permutation[idx+1] is 1
Look for T and check if permutation[idx+1] is either 1 or 2.
You only print the solution if these conditions are met!
...
if(curIndex == permutationLength){
if (solutionValid()) {
displayPermutation(permutation,permutationLength);
}
}
...
https://mathworld.wolfram.com/Permutation.html
https://mathworld.wolfram.com/Combination.html
Do you mean a recursion like this?
function f(n, str=""){
if (!n)
return [str];
let result = [];
if (n >= 3)
result = result.concat(f(n - 3, str + "1R1"));
if (n >= 2)
result = result
.concat(f(n - 2, str + "T1"))
.concat(f(n - 2, str + "T2"));
return result
.concat(f(n - 1, str + "1"))
.concat(f(n - 1, str + "2"));
}
console.log(f(3));
I have a small problem where I need to find all possible paths given a set of numbers. For example, lets say we have numbers 1, 2 and 3. I need to find all the possible combinations. The result for this simple case is:
path_1 = 1
path_2 = 2
path_3 = 3
path_4 = 1, 2
path_5 = 1, 3
path_6 = 2, 3
path_7 = 1, 2, 3
It is simple to see that the number of paths is (2^n)-1, so for 3 elements, it is 7, and so on. It is quite simple to do this by hand for a small number of elements, but as the numbers get big, it gets harder and harder.
Someone has suggested that I can use the boost graph library for this problem, but am not quite sure how to do as I do not have enough experience with it. Any help would be much appreciated.
Thanks in advance
template< class It >
void compute_all_possible_paths( path_collection_t& res, It b, It e ) {
std::size_t curVecSize = res.size();
for( std::size_t i = 0; i < curVecSize; i++ ) {
path_t p;
p.reserve( res[i].size() + 1 );
std::copy( res[i].begin(), res[i].end(), std::back_inserter(p) );
p.push_back( *b );
res.push_back( p );
}
path_t p;
p.push_back( *b );
res.push_back( p );
if( ++b == e ) return ;
compute_all_possible_paths( res, b, e );
}