My teacher gave me a homework: -
You are given a string (of at most 100 characters) consisting of lowercase characters, terminated with a # symbol. The string is the "encrypted" form of an original string as follows - each character in the original string has been shifted by a fixed integer n (where 1<= n <= 25).
Assuming that a is the 0th character in the alphabet, ..., and z is the 25th character of the alphabet, in the encrypted version, we have:
'a' becomes the nth character in the alphabet, 'b' becomes the (n+1)%26th character in the alphabet, ...
and so on.
n is not known to you. You only know that the first character represents 't'. From this information, you have to output the original string.
Here is my code for it: -
#include <stdio.h>
#include <stdlib.h>
int main()
{
char array[100]; //string will be stored here.
char a;
int i = 0; //initializer
scanf("%c", &a);
int counter = 0; // to count the size of the string
while(a != '#')
{
array[i] = a;
i++;
counter++;
scanf("%c", &a);
}
a = 't';
int temp = a - array[0]; //To get the offset of the encrypted message.
for(i = 0; i < counter; i++)
{
if((array[i] + temp) >= 'a' && (array[i] + temp) <= 'z')
{
array[i] = array[i] + temp; //Applying the offset to the encrypted message to get the true message.
}
else if((array[i] + temp) < 'a')
{
array[i] = 'z' - (temp - (array[i] - 'a')); //If offset is large and it falls below ASCII charater number 'a' then loop back to 'z'.
}
else
{
array[i] = 'a' + (temp - ('z' - array[i])); //If offset is large and it falls above ASCII charater number 'z' then loop back to 'a'.
}
}
puts(array);
return 0;
}
Now the logic which I have applied is certainly wrong. All of my test cases are failing. I suspect the problem is in the following snippet of the code: -
for(i = 0; i < counter; i++)
{
if((array[i] + temp) >= 'a' && (array[i] + temp) <= 'z')
{
array[i] = array[i] + temp; //Applying the offset to the encrypted message to get the true message.
}
else if((array[i] + temp) < 'a')
{
array[i] = 'z' - (temp - (array[i] - 'a')); //If offset is large and it falls below ASCII charater number 'a' then loop back to 'z'.
}
else
{
array[i] = 'a' + (temp - ('z' - array[i])); //If offset is large and it falls above ASCII charater number 'z' then loop back to 'a'.
}
}
I am not able to do this easy question. Please help.
Some of the test cases are mentioned below: -
#include <stdio.h>
#include <stdlib.h>
int main()
{
char array[100]; //string will be stored here.
int counter = 0; // to count the size of the string
scanf("%s", array);
while(array[counter] != '#')
counter++;
int offset = 't' - array[0]; //To get the offset of the encrypted message.
if (offset < 0)
offset = 26 + offset;
for(int i = 0; i < counter; i++)
array[i] = ((array[i] - 'a' + offset) % 26) + 'a';
array[counter] = '\0'; // Removing the # at the end
puts(array);
return 0;
}
One, you weren't null terminating your string, hence the extra characters at the end at times. I fixed it by asking for a string instead of several characters in a row, which I find a lot simpler personally. It then adds a \0
at the end of your array automatically, while it doesn't with your character by character approach. That being said, yours can also work, but only if you add a array[counter] = '\0'
at the end to null terminate it.
'a' becomes the nth character in the alphabet, 'b' becomes the (n+1)%26th character in the alphabet, ...
Your implementation was vastly overcomplicated. A single line with array[i] = ((array[i] - 'a' + offset) % 26) + 'a';
is all that's required here.
To break it down, we get the index of the letter in the alphabet by doing array[i] - 'a'
; we add the offset, we run a modulo 26 (the number of characters in the alphabet) to get the index of the new letter and to account for the fact we can get a result higher than 26 (in which case we want to wrap around : a 29 would mean the third letter of the alphabet, because 29 - 26 = 3). We add back 'a'
to get an actual character instead of an index, and we've got the correct letter.
Also, your code is currently susceptible to buffer overflows! You shouldn't care too much for a school project, but it's a gaping security flaw you may be interested in learning more about. You're also highly susceptible to a segmentation fault if there's no # in your input string.
I've purposefully not improved upon your code too much as it is a school project, but know the variable counter
is wholly unnecessary, and you can redesign your code to avoid having to use it. You may want to think about how to go about this.
"Your implementation was vastly overcomplicated." Well, let's have a look at yours then. There is absolutely no reason to copy anything, or to loop over the string twice, or to use modulo. You could just do a replacement on the spot in the first go.
@OctaveL thank you so much for your insightful answer. Now I am able to understand everything. However I still am not able to understand how you took out offset, using only 1 line
(int offset = ('t' + array[0] - 'a' * 2) % 26;)
. If possible please do explain this also.@Cheatah if you think that you are able to provide a simpler answer than the one mentioned above, please do post it, I am all ears.
@Cheatah I agree it's not the best, but I'd argue it's because I tried not to tweak the code too much. For instance, I'm well aware calculating
counter
is unnecessary, but I kept it because that's how Swarnim chose to implement it. I admit I should have made that clearer, but I'm not willing to provide a much better solution as this is a school project where complex code would stand out and make it seem like OP got handed out an answer. I am curious as to why modulo is unnecessary, though ; I considered a solution that didn't use it, but I preferred it as it's mentioned in the homework.@SwarnimKhosla About that line; sorry to say I removed it from my post as it was invalid, or rather only worked for 75% of possible offsets. I'm convinced it's possible to do in one line without a ternary operator (which you may very well use here), but I have to find a better formula than this one. Sorry about the confusion.