CS50 Problem Set 2: Substitution (Need Help) - cs50

Im facing some issue here. Can anyone tell me what is wrong with my code?
This is the check50 result:
:) substitution.c exists
:) substitution.c compiles
:( encrypts "A" as "Z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
expected "ciphertext: Z...", not ""
:( encrypts "a" as "z" using ZYXWVUTSRQPONMLKJIHGFEDCBA as key
expected "ciphertext: z...", not ""
:( encrypts "ABC" as "NJQ" using NJQSUYBRXMOPFTHZVAWCGILKED as key
expected "ciphertext: NJ...", not ""
:( encrypts "XyZ" as "KeD" using NJQSUYBRXMOPFTHZVAWCGILKED as key
expected "ciphertext: Ke...", not ""
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZTEOGXHCIPJSQD as key
expected "ciphertext: Cb...", not ""
:( encrypts "This is CS50" as "Cbah ah KH50" using yukfrnlbavmwzteogxhcipjsqd as key
expected "ciphertext: Cb...", not ""
:( encrypts "This is CS50" as "Cbah ah KH50" using YUKFRNLBAVMWZteogxhcipjsqd as key
expected "ciphertext: Cb...", not ""
:( encrypts all alphabetic characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
expected "ciphertext: Rq...", not ""
:( does not encrypt non-alphabetical characters using DWUSXNPQKEGCZFJBTLYROHIAVM as key
expected "ciphertext: Yq...", not ""
:) handles lack of key
:) handles too many arguments
:) handles invalid key length
:) handles invalid characters in key
:) handles duplicate characters in key
:) handles multiple duplicate characters in key
This is my code:
#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(int argc, string argv[])
{
string alphabet= "abcdefghijklmnopqrstuvwxyz";
if(argc != 2)
{
printf("missing/more than 1 command-line argument\n");
return 1;
}
//check if there are 26 characters
int a= strlen(argv[1]);
if(a!=26)
{
printf("key must contain 26 characters\n");
return 1;
}
//Check if characters are all alphabetic
for(int i=0, n=strlen(argv[1]); i<n; i++)
{
if(!isalpha(argv[1][i]))
{
printf("only alphabetic characters allowed\n");
return 1;
}
//check if each letter appear only once
for(int j=1; j<n; j++)
{
if(argv[1][i]==argv[1][j])
{
printf("repeated alphabets not allowed\n");
return 1;
}
}
}
//prompt user for plaintext
string b= get_string("plaintext: \n");
int m=strlen(b);
char ciphertxt[m+1];
//find out the alphabetical position of each character in string b (i.e character c in string b has alphabetical position of 3)
for(int k=0; k<m; k++)
{
for(int p=0, q=strlen(alphabet); p<q; p++)
{
if(b[k]==alphabet[p])
{
ciphertxt[k]= tolower(argv[1][p]);
break;
}
else if(b[k]==(alphabet[p]-32))
{
ciphertxt[k]= toupper(argv[1][p]);
break;
}
else
{
ciphertxt[k]= b[k];
}
}
}
ciphertxt[m]='\0';
//print ciphertext
printf("ciphertext: %s\n", ciphertxt);
return 0;
}

Did you run your code using the tests cs50 shows you? I did; it does not encrypt anything; it always gives "repeated alphabets not allowed" message.
The problem is in the j loop. It will always report the 2nd letter of argv[1] as a duplicate. That is because i and j are both 1 therefore this if(argv[1][i]==argv[1][j]) always evaluates to true.

There are a several approaches to solving this problem. I will not solve them for you using the C programming language. You must do that yourself. Following is an approach that works very efficiently using the Ada programming language, but is not easily accomplished using the C programming language.
In Ada a string is defined as
type string is array (Positive range <>) of Character;
Thus, a string is an unconstrained array type, meaning instances of an array may be any length. Ada arrays require the programmer to define the range of values for the array index. Index values need not start at 0. Index values may start at any value which is valid for the type declared to be the index type. Index types may be integer types or enumeration types. Ada characters are an enumeration type, which allows the programmer to index an array using characters.
The following example uses many of the features described above.
with Ada.Command_Line; use Ada.Command_Line;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Characters.Handling; use Ada.Characters.Handling;
procedure substitution is
subtype lower is Character range 'a' .. 'z';
subtype upper is Character range 'A' .. 'Z';
subtype sequence is String (1 .. 26);
alphabet : constant array (lower) of Positive :=
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26);
function substitute (Char : Character; Key : sequence) return Character is
begin
if Char in lower then
return To_Lower (Key (alphabet (To_Lower (Char))));
elsif Char in upper then
return To_Upper (Key (alphabet (To_Lower (Char))));
else
return Char;
end if;
end substitute;
function is_duplicate (char : Character; Key : sequence) return Boolean is
count : Natural := 0;
begin
for C of Key loop
if C = char then
count := count + 1;
end if;
end loop;
return count > 1;
end is_duplicate;
Key : String (1 .. 26);
Invalid_Argument_Error : exception;
begin
if Argument_Count /= 1 then
Put_Line ("There must be exactly one command line argument.");
raise Invalid_Argument_Error;
end if;
if Argument (1)'Length /= 26 then
Put_Line ("The argument must contain 26 characters.");
raise Invalid_Argument_Error;
else
Key := Argument (1);
end if;
for C of Key loop
if is_duplicate (C, Key) then
Put_Line ("The argument cannot contain duplicate values.");
raise Invalid_Argument_Error;
end if;
end loop;
for C of Key loop
if not (C in lower or else C in upper) then
Put_Line ("The argument must contain only alphabetic characters.");
raise Invalid_Argument_Error;
end if;
end loop;
Put_Line ("Enter plain text:");
declare
input : String := Get_Line;
cipher : String (input'Range);
begin
for I in input'Range loop
cipher (I) := substitute (input (I), Key);
end loop;
Put_Line ("cipher text: " & cipher);
end;
end substitution;
Ada allows the starting procedure for a program to be named whatever the programmer wants to name it. In C the starting function must be named "main". In this example the starting procedure is named "substitution". Ada characters are full eight bit characters and represent the Latin-1 character set. The lower seven bits of the Latin-1 character set is the same as the ASCII character set. Thus, there are some lower case characters in the Latin-1 character set which are not part of the ASCII character set. For this reason the program defines the upper case characters and lower case characters unique to the ASCII character set by declaring two subtypes of the type character.
subtype lower is Character range 'a' .. 'z';
subtype upper is Character range 'A' .. 'Z';
The syntax 'a' .. 'z' defines a range of values and includes all the characters starting with 'a' and ending with 'z'.
A subtype of the Ada string type is named sequence and is declared to be a string indexed by the value range 1 .. 26. Thus, each instance of the subtype sequence must contain a 26 character string. Ada does not append a null character to the end of its strings.
The array named alphabet is defined to be a constant array indexed by the subtype lower. Each element of the array is an integer with a minimum value of 1. The array is initialized to the numbers 1 through 26 with 1 indexed by 'a' and 26 indexed by 'z'. This array is used as a look-up table for indexing into the key entered on the program command line.
The function named substitute takes two parameters; Char, which is a Character and Key which is a sequence (a 26 character string). Substitute returns the encrypted character value.
The function returns the character in the Key parameter indexed by the number which is indexed by the letter in the parameter Char. The array named alphabet becomes the look-up table for the index value corresponding to the character contained in the parameter Char.
The function named is_duplicate is used to determine if a character occurs more than once in a Key sequence. It simply counts the number of times the character in the Char parameter occurs in the Key sequence. The function returns TRUE if the count is greater than 1 and false if the count is not greater than 1.
After performing the necessary checks on the command-line parameter the program prompts for a string to encrypt and then simply assigns to the string cipher the encrypted character corresponding to each input character.

Related

golang for loop a string, but it prints 'char' as int, why?

A very simple go function:
func genString(v string) {
for _, c := range v {
fmt.Println(c)
}
}
Called in:
func TestBasics(t *testing.T) {
genString("abc")
}
Then I ran:
go test -v -run TestBasics xxxxxx
It prints:
97
98
99
I expected that it should print
a
b
c
But it prints the corresponding integer value? Why, how to fix it and print just the char?
Thanks!
Why
Looping with range over string will give you a sequence of runes.
For range spec:
Range expression 1st value 2nd value
array or slice a [n]E, *[n]E, or []E index i int a[i] E
string s string type index i int see below rune
map m map[K]V key k K m[k] V
channel c chan E, <-chan E element e E
(notice the second row and last column in the table)
For a string value, the "range" clause iterates over the Unicode code points in the string starting at byte index 0. On successive
iterations, the index value will be the index of the first byte of
successive UTF-8-encoded code points in the string, and the second
value, of type rune, will be the value of the corresponding code
point. If the iteration encounters an invalid UTF-8 sequence, the
second value will be 0xFFFD, the Unicode replacement character, and
the next iteration will advance a single byte in the string.
A rune value is an integer value identifying a Unicode code point.
The type itself is just an alias of int32.
how to fix it and print just the char
Use fmt.Printf with the %c verb to print the character value, i.e. fmt.Printf("%c\n", c)
fmt printing verbs doc:
Integers:
%b base 2
%c the character represented by the corresponding Unicode code point
%d base 10
%o base 8
%O base 8 with 0o prefix
%q a single-quoted character literal safely escaped with Go syntax.
%x base 16, with lower-case letters for a-f
%X base 16, with upper-case letters for A-F
%U Unicode format: U+1234; same as "U+%04X"
(notice the second row in the table)
for _, c := range "abc" {
fmt.Printf("%c\n", c)
}
https://go.dev/play/p/BEjJof4XvIk

Leetcode Problem First Unique Character In a string

I am struggling to understand how the leetcode solution for the above problem works. If any help on how the post increment operator is working on the value of the array it would be great.
class Solution {
public int firstUniqChar(String s) {
int [] charArr = new int[26];
for(int i=0;i<s.length();i++){
charArr[s.charAt(i)-'a']++;
}
for(int i=0;i<s.length();i++){
if(charArr[s.charAt(i)-'a']==1) return i;
}
return -1;
}
The problem link here https://leetcode.com/problems/first-unique-character-in-a-string/submissions/!
First, you need to understand there are 26 letters in the English alphabet. So the code creates an array of 26 integers that will hold the count of each letter in the string.
int [] charArr = new int[26];
The count of all a's will be at index 0, the cound of all b's at index 1, etc. The default value for int is 0, so this gives an array of 26 zeros to start with.
Each letter has two character codes; one for upper case and one for lower case. The function String.charAt() returns a char but char is an integral type so you can do math on it. When doing math on a char, it uses the char code. So for example:
char c = 'B';
c -= 'A';
System.out.println((int)c); // Will print 1 since char code of 'A' = 65, 'B' = 66
So this line:
charArr[s.charAt(i)-'a']++;
Takes the char at i and subtracts 'a' from it. The range of lower case codes are 97-122. Subtracting 'a' shifts those values to 0-25 - which gives the indexes into the array. (Note this code only checks lower case letters).
After converting the character to an index, it increments the value at that index. So each item in the array represents the character count of the corresponding letter.
For example, the string "aabbee" will give the array {2, 2, 0, 0, 2, 0, 0....0}

Numbered String to Int with leading zeros

For a numbered string (1234) I normally use the package strconv with the function Atoi to convert from string to int in Go. However, what is the idiomatic way of approaching this if the numbered string starts with leading zeros (e.g. 01234)?
Slicing the string and then converting the []string to []int, is one approach, but is it the best/idiomatic way Go-ing about it?
Update 1:
From input string 01234, expected output 01234 as any type int (in any kind of simple or composed type of int, e.g. foo := "01234" to bar = []int{0, 1, 2, 3, 4}).
Is there an idiomatic (or standard package func) to approach this problem, or is the string to rune/byte conversation (do some business logic and then convert back) necessary when the variable has one or more leading zeros (e.g. 01234 or 00001).
Update 2: Just to make it completely clear, as I can feel the question can be clarified future: foo, _ := strconv.Atoi("01234") returns 1234 as an int (and same result can be obtained in other ways with strings package etc.).
The question here is, how (if possible, in Go idiomatic) can I get the result with the leading zeros, that is, 01234 (and NOT 1234) in type int?
use strings.TrimLeft(s, "0") to remove leading zeroes from the string.
fmt.Printf("%01d ", 5) // 5
fmt.Printf("%02d ", 5) // 05
fmt.Printf("%03d ", 5) // 005
fmt.Printf("%04d ", 5) // 0005
myInt := fmt.Sprintf("%05d ", 5)
fmt.Println(myInt) // 00005
https://pkg.go.dev/fmt#pkg-overview
To convert a string of decimals to a slice of integers for those decimal values, loop over the string and convert the rune to the corresponding integer using subtraction:
func toIntSlice(s string) ([]int, error) {
var result []int
for _, r := range s {
if r < '0' || r > '9' {
return nil, fmt.Errorf("not an integer %s", s)
}
result = append(result, int(r-'0'))
}
return result, nil
}
Example use:
foo := "01234"
bar, _ := toIntSlice(foo)
// bar is []int{0, 1, 2, 3, 4}
https://go.dev/play/p/etHtApYoWUi

How do I type a string containing less than 5 characters and still continue to the next row of code?

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
procedure Help is
F: Float
S: String(1..6);
begin
Put("Type a string with max 5 characters: ");
Get_Line(S(1..5), I);
Put("You typed the string: ");
Put(S(1..I));
Skip_Line;
New_Line;
Put("Type a string with max 5 characters: ");
Get_Line(S(1..5), I);
Put("You typed the string: ");
Put(S(1..I));
end Help;
When I run this code, and for instance type "Hey brother" I get "Hey b" as an output, and then it continues to the next problem. All good and clear!
But when I type a string containing less than 5 characters like "Hey", it types it out like expected BUT it is still waiting for me to fill out the string containing 5 characters. It's supposed to jump to the next problem but it doesn't. If I now type "ss", you will get
"Heyss" and then it continues to the next problem. So how do I do this? How do I make it as if even I have less than 5 characters it will not only type it out but also continue to the next problem? I've kind of figured out that i have to use simple "if sequences" but I have no clue on how I should apply it as all of my attempts have failed.
My problem should be able to execute all of these inputs and give the following outputs:
Tye a string with max 5 characters: Hi
You typed the string: Hi
Tye a string with max 5 characters: Hello
You typed the string: Hello
Tye a string with max 5 characters: Hey there
You typed the string: Hey t
Tye a string with max 5 characters:
You typed the string:
The Get_Line function takes two parameters named Item, which is a string and Last which is an instance of Natural.
Get_Line will read the input to the end of the line or the end of the string, whichever comes first. The Last parameter is an OUT parameter returning the index value of the last character read into the string.
Try the following approach:
with Ada.Text_IO; use Ada.Text_IO;
procedure help is
Input : String (1..80); -- It need not be only 5 characters
Length : Natural;
Num_Good : Natural := 0;
begin
while Num_Good < 2 loop
Put ("Enter a string of 5 characters: ");
Get_Line (Item => Input, Last => Length);
if Length = 5 then
Put_Line (Input (1..Length));
Num_Good := Num_Good + 1;
else
Put_Line ("Error: Input does not contain exactly 5 characters.");
end if;
end loop;
end help;
Following is a version that uses only the features you describe:
with Ada.Text_IO; use Ada.Text_IO;
procedure help2 is
S : String(1..80);
I : Integer;
J : Integer := 0;
begin
loop
Put("Enter a string containing 5 characters: ");
Get_Line (S, I);
if I = 5 then
J := J + 1;
Put_Line (S(1..I));
end;
if J = 2 then
exit;
end if;
end loop;
end help2;
The program will read the string input by the user and output the string if it contains exactly 5 characters. Nothing will be output if the string does not contain exactly 5 characters. The loop exits when the user successfully enters two strings containing exactly 5 characters.
The following version accepts a string of up to 5 characters.
with Ada.Text_IO; use Ada.Text_IO;
procedure Up_To_five is
S : String (1..5);
I : Integer;
begin
Put ("Enter a string with a max of 5 characters: ");
Get_Line (S, I);
Skip_Line;
Put ("You typed the string: ");
Put_Line (S(1..I));
New_Line;
Put ("Enter a string with a max of 5 characters: ");
Get_Line (S, I);
Skip_Line;
Put ("You typed the string: ");
Put_Line (S(1..I));
end Up_To_five;
Your program says
Put("Type a string with max 5 characters: ");
Get_Line(S(1..5), I);
and you type hi and press RET (the return key). Get_Line returns, having consumed h i RET, setting S (1 .. 2) to hi, which you print out, and I to 2.
Now, your program says
Skip_Line;
which according to ARM A.10.5(9)
... Reads and discards all characters until a line terminator has been read, ...
and so sits there waiting for another RET.
If on the other hand you type 5 or more characters, Get_Line finishes before needing to read the RET, so it’s still there in the input buffer.
So you need to decide whether or not to call Skip_Line.

Looping string characters?

How can I read each character in a String? For example, I want to read each character in String "a7m4d0". After that I want to verify that each character is a character or a number. Any tips or ideas?
DATA: smth TYPE string VALUE `qwert1yua22sd123bnm,`,
index TYPE i,
length TYPE i,
char TYPE c,
num TYPE i.
length = STRLEN( smth ).
WHILE index < length.
char = smth+index(1).
TRY .
num = char.
WRITE: / num,'was a number'.
CATCH cx_sy_conversion_no_number.
WRITE: / char,'was no number'.
ENDTRY.
ADD 1 TO index.
ENDWHILE.
Here's your problem solved :P
A bit convoluted and on a recent 740 ABAP server. :)
DATA: lv_text TYPE string VALUE `a7m4d0`.
DO strlen( lv_text ) TIMES.
DATA(lv_single) = substring( val = lv_text off = sy-index - 1 len = 1 ) && ` is ` &&
COND string( WHEN substring( val = lv_text off = sy-index - 1 len = 1 ) CO '0123456789' THEN 'Numeric'
ELSE 'Character' ).
WRITE : / lv_single.
ENDDO.
Here is how you can access a single character within a string:
This example will extract out the character "t" into the variable "lv_char1".
DATA: lv_string TYPE char10,
lv_char TYPE char1.
lv_string = "Something";
lv_char1 = lv_string+4(1).
Appending "+4" to the string name specifies the offset from the start of the string (in this case 4), and "(1)" specifies the number of characters to pick up.
See the documentation here for more info:
http://help.sap.com/saphelp_nw04/Helpdata/EN/fc/eb341a358411d1829f0000e829fbfe/content.htm
If you want to look at each character in turn, you could get the length of the field using "strlen( )" and do a loop for each character.
One more approach
PERFORM analyze_string USING `qwert1yua22sd123bnm,`.
FORM analyze_string USING VALUE(p_string) TYPE string.
WHILE p_string IS NOT INITIAL.
IF p_string(1) CA '0123456798'.
WRITE: / p_string(1) , 'was a number'.
ELSE.
WRITE: / p_string(1) , 'was no number'.
ENDIF.
p_string = p_string+1.
ENDWHILE.
ENDFORM.
No DATA statements, string functions or explicit indexing required.
I know the post it's old but this might be useful, this is what use :)
DATA lv_counter TYPE i.
DO STRLEN( lv_word ) TIMES.
IF lv_word+lv_counter(1) CA '0123456789'
"It's a number
ENDIF.
lv_counter = lv_counter + 1.
ENDDO.

Resources