C - Cycle through all possible lowercase strings - c

I'm learning C with the CS50 course problem set 2, using the crypt function to brute force guess a password. Currently writing a function that prints all possible strings of a certain length, eg:
aa
ab
...
az
ba
...
zy
zz
I've written a fairly simple recursive function to do so:
#include <cs50.h>
#include <stdio.h>
#include <crypt.h>
#include <string.h>
void stringcycler(int n, int passLength, char *pass)
// Scrolls through all lowercase letter combinations for a string of length passLength
// Expects an integer value of the length of the strng as both n and passLength
// Also expects a char* array of length passLength with all chars set to 'a' (and a null character)
{
if(n != 0)
{
for(pass[passLength - n] = 'a'; pass[passLength - n] < 'z'; pass[passLength - n]++)
{
stringcycler(n-1, passLength, pass);
printf("%s\n", pass);
// return 0;
}
}
}
int main()
{
// Initialise char *c, and scroll through letters
int passLength = 2; // The number of characters you want to brute force guess
char pass[passLength + 1]; // Add 1 for the null character
int i;
for(i = 0; i < passLength; i++) pass[i] = 'a'; // Set every char in pass to 'a'
pass[passLength] = '\0'; // Set null character at the end of string
stringcycler(passLength, passLength, pass);
return 0;
}
It works for the most part, but only goes to yz. Whenever it sees a z it basically skips, so it goes to yz, then never does za to zz. If I add an = to the for loop line:
pass[passLength - n] < 'z';
ie.
pass[passLength - n] <= 'z';
Then it prints '{' characters in the mix. Any help? And another question is, how can I change this to work for all combos of upper and lower case too, is there a neat way of doing it?

You print after you return from you recursion, but you should print when the recursion has reached the end (or beginning, in your case) of the string. In other words, printing should be an alternative branch to recursing:
void stringcycler(int n, int len, char *pass)
{
if (n != 0) {
for (pass[len - n] = 'a'; pass[len - n] <= 'z'; pass[len - n]++) {
stringcycler(n - 1, len, pass);
}
} else {
printf("%s ", pass);
}
}
The if part constructs the strings as it recurses further down. The else part does something with the constructed string. (Of course, you must include 'z' in your loop. Your original code only prints the z in the last place, because it prints after ther recursion returns, which means thet the char buffer is in a condition that wouldn't (re-)enter the loop.)

Below is a generic backtracking algorithm for generating the password. The idea here is to imagine filling the slots for a given char array a. We will be generating the possible candidates for the given position k for the array a. I have taken the candidates as lower case ascii letters a-z and upper case ASCII letters A-Z. If you want to include other ASCII characters, just modify the construct_candidates function accordingly.
Once the array is filled i.e. k becomes PASS_LEN, we know we have generated the password, we can process it however we like, I have just printed the password here.
The value of PASS_LEN macro can be adjusted to generate password of any desired length.
#include <stdio.h>
#include <stdlib.h>
#define PASS_LEN 2
static char* construct_candidates (char a[], int k, int *count)
{
/* Lower case ASCII */
int min1 = 97;
int max1 = 122;
/* Upper case ASCII */
int min2 = 65;
int max2 = 90;
*count = (max1 - min1 + 1) + (max2 - min2 + 1);
char *cand = calloc(*count, sizeof(char));
if (cand == NULL) {
printf("malloc failed\n");
return NULL;
}
int idx = 0;
for (int i = min1; i <= max1; i++) {
cand[idx] = i;
idx++;
}
for (int i = min2; i <= max2; i++) {
cand[idx] = i;
idx++;
}
return cand;
}
static void backtrack(char a[], int k)
{
int i;
if (k == PASS_LEN) {
for (i = 0; i < PASS_LEN; i++) {
printf("%c", a[i]);
}
printf("\n");
return;
}
int cand_count = 0;
char *cand = construct_candidates(a, k, &cand_count);
if (cand == NULL) {
printf("Failed to get candidates\n");
return;
}
for (i = 0; i < cand_count; i++) {
a[k] = cand[i];
backtrack(a, k + 1);
}
free(cand);
}
int main()
{
char a[PASS_LEN] = {'\0'};
backtrack(a, 0);
}

Related

%s not working properly with my return value, in C

As the title says %s is not working properly This is for a code wars so %s needs to be able to work with the array to pass the sample test cases; Cannot change function declaration of playPass. Using Ascii table. Also the for loop to print in main() works and gives me correct output.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// takes a string and shifts letter n value;
// lower cases every other element if it contains a letter
// replaces number with a 9 complement
// returns array with values reversed.
char* playPass(char* s, int n)
{
int length = strlen(s);
char *pass= (char *)malloc(length*sizeof(char)+1);
char a;
int letter, i;
for(i=0; i< length; i++)
{
a = s[i];
letter = a;
if( letter >= 65 && letter <=90 )
{
letter +=n;
if(letter >90)
{
letter -=90;
letter +=64;
}
if((i+1) % 2 == 0 )
{
letter += 32;
}
a = letter;
}
else if(letter >= 48 && letter <= 57)
{
letter -= 48;
letter = 9 - letter;
a = letter + '0';
}
pass[length - i] = a;
}
return pass;
}
// answer should be
int main (){
char* s ={"I LOVE YOU!!!"};
int length = strlen(s);
int k = 1;
s =playPass( s,k );
int i;
printf(" %s", s);
for(i = 0; i <= length; i++)
{
printf(" %c", s[i]);
}
}
%s works only with null terminated char *
char* playPass(char* s, int n) {
…
for() {
…
}
pass[i] = '\0'; //Null terminate here.
return pass;
}
so figured it out.
the end where i assined the new value to the new array
pass[length - i] = a;
made it to where it never wrote a value to the first element so
pass[0]= NULL;
had to change it to
pass[length - (i-1)] = a;
thanks for the help everyone, I also cleaned up the code from the magic numbers Great tip #phuclv!

How to avoid duplicates when finding all k-length substrings

I want to display all substrings with k letters, one per line, but avoid duplicate substrings. I managed to write to a new string all the k length words with this code:
void subSent(char str[], int k) {
int MaxLe, i, j, h, z = 0, Length, count;
char stOu[1000] = {'\0'};
Length = (int)strlen(str);
MaxLe = maxWordLength(str);
if((k >= 1) && (k <= MaxLe)) {
for(i = 0; i < Length; i++) {
if((int)str[i] == 32) {
j = i = i + 1;
} else {
j = i;
}
for(; (j < i + k) && (Length - i) >= k; j++) {
if((int)str[j] != 32) {
stOu[z] = str[j];
} else {
stOu[z] = str[j + 1];
}
z++;
}
stOu[z] = '\n';
z++;
}
}
}
But I'm struggling with the part that needs to save only one time of a word.
For example, the string HAVE A NICE DAY
and k = 1 it should print:
H
A
V
E
N
I
C
D
Y
Your subSent() routine poses a couple of challenges: first, it neither returns nor prints it's result -- you can only see it in the debugger; second it calls maxWordLength() which you didn't supply.
Although avoiding duplicates can be complicated, in the case of your algorithm, it's not hard to do. Since all your words are fixed length, we can walk the output string with the new word, k letters (plus a newline) at a time, doing strncmp(). In this case the new word is the last word added so we quit when the pointers meet.
I've reworked your code below and added a duplication elimination routine. I didn't know what maxWordLength() does so I just aliased it to strlen() to get things running:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define maxWordLength strlen
// does the last (fixed size) word in string appear previously in string
bool isDuplicate(const char *string, const char *substring, size_t n) {
for (const char *pointer = string; pointer != substring; pointer += (n + 1)) {
if (strncmp(pointer, substring, n) == 0) {
return true;
}
}
return false;
}
void subSent(const char *string, int k, char *output) {
int z = 0;
size_t length = strlen(string);
int maxLength = maxWordLength(string);
if (k >= 1 && k <= maxLength) {
for (int i = 0; i < length - k + 1; i++) {
int start = z; // where does the newly added word begin
for (int j = i; (z - start) < k; j++) {
output[z++] = string[j];
while (string[j + 1] == ' ') {
j++; // assumes leading spaces already dealt with
}
}
output[z++] = '\n';
if (isDuplicate(output, output + start, k)) {
z -= k + 1; // last word added was a duplicate so back it out
}
while (string[i + 1] == ' ') {
i++; // assumes original string doesn't begin with a space
}
}
}
output[z] = '\0'; // properly terminate the string
}
int main() {
char result[1024];
subSent("HAVE A NICE DAY", 1, result);
printf("%s", result);
return 0;
}
I somewhat cleaned up your space avoidance logic but it can be tripped by leading spaces on the input string.
OUTPUT
subSent("HAVE A NICE DAY", 1, result);
H
A
V
E
N
I
C
D
Y
subSent("HAVE A NICE DAY", 2, result);
HA
AV
VE
EA
AN
NI
IC
CE
ED
DA
AY
subSent("HAVE A NICE DAY", 3, result);
HAV
AVE
VEA
EAN
ANI
NIC
ICE
CED
EDA
DAY

Caesar code in C extra letter for result

#include <stdio.h>
void caesar(char bemenet[], char eredmeny[], int n){
int i = 0;
for(i = 0; bemenet[i] != '\0'; i++) {
if(bemenet[i] == 'z') {
eredmeny[i] = 'a';
eredmeny[i] += n-1;
}
else
{
eredmeny[i] += n;
}
}
eredmeny[i] = '\0';
}
int main(){
char tomb1[]="caesarkodolas";
char tomb2[]="";
caesar(tomb1,tomb2,1);
printf("%s \n",tomb2);
return 0;
}
My out for the "eredmeny" (result) this:
"dbftbslpepmb" but tomb2=> ☺dbftbslpepmb it's not OK.. cause I have an extra char |☺|..
Allocate enough memory for the second parameter, and change this line
eredmeny[i] += n;
to this:
eredmeny[i] = bemenet[i] + n;
Note that this is not a bulletproof implementation of Caesar cipher: it would work for n==1, but it will break for larger n.
You need to think of a different way of implementing the "wrap-around": rather than testing for 'z' and replacing it with 'a', compute the new position of a letter modulo 26, and then add a to it:
void caesar(char bemenet[], char eredmeny[], int n){
int i;
for(i = 0; bemenet[i] != '\0'; i++) {
// Compute the position of the replacement letter
int pos = (bemenet[i] - 'a' + n) % 26;
// Place the letter into the output.
eredmeny[i] = 'a' + pos;
}
eredmeny[i] = '\0';
}
demo.
First of all you should have tomb2 big enough to store result.
For example, as mentioned above
char tomb2[255] = {0};
Also you have error here
else
{
eredmeny[i] += n;
}
You have to assign valid ASCII value to eredmeny[i] so change this string to
eredmeny[i] += bemenet[i] + n
Also it usually bad practice to pass a pointer on array without passing its size. Easy to get buffer overflow.
you're not doing the maths right.
if you are using just lower case letters then you need to add n, but then many letters will be "after" z, so you need to start again at a.
you want something more like this:
for(i = 0; bemenet[i] != '\0'; i++) {
int encrypted = bemenet[i] + n;
if (encrypted > 'z') encrypted = encrypted - 'z' + 'a';
eredmeny[i] = (char)encrypted;
}
(and also fix the output array size as described in other answers here).

Can't get a function call in a function to work properly

I'm writing a program to generate a string of random uppercase letters, then take user input of uppercase letters, along with a character form the user. For any instance of the user input letter in the random string, it replaces that letter with the character entered by the user.
For example, s1 = {BDHFKYL} s2 = {YEIGH} c = '*'
Output = BD*FK*L
The program was working correctly until I added the feature to ask the user to enter what character they would like to replace the letters.
The output is:
Please enter at least 2 capital letters and a maximum of 20.
HDJSHDSHDDS
HDJSHDSHDDS
Enter a character to replace occuring letters.
*
NWLRBBMQB
Would you like to enter another string?
Here's the code:
void fillS1(char x[]);
void fillS2(char x[], char y[], char z);
void strFilter(char a[], char b[], char c);
int main(int argc, const char * argv[])
{
char s1[42];
char s2[22];
char x = 0;
fillS2(s2, s1, x);
return 0;
}
void fillS1(char x[])
{
for (int i = 0; i < 40; i++)
x[i] = 'A' + random() % 26;
x[40] = (char)0;
}
void fillS2(char x[], char y[], char z){
char loopContinue = 0;
do {
int i = 0;
int capitalLetterCheck = 0;
printf("Please enter at least 2 capital letters and a maximum of 20.\n");
while (( x[i] = getchar()) != '\n' ) {
i++;
}
x[i] = '\0';
if (i < 3) {
printf("You need at least two letters\n");
}
else if (i > 21){
printf("You cannot have more than twenty letters\n");
}
for (i = 0; i < 20; i++) {
if ((x[i] >= 'a') && (x[i] <= 'z')) {
printf("You many only have capital letters.\n");
capitalLetterCheck = 2;
}
}
if (capitalLetterCheck != 2) {
for (i = 0; i < 20; i++) {
if ((x[i] >= 'A') && (x[i] <= 'Z')) {
puts(x);
fillS1(y);
printf("Enter a character to replace occuring letters.\n");
while ((z = getchar() != '\n')) {
}
strFilter(y, x, z);
break;
}
}
}
printf("Would you like to enter another string?\n");
gets(&loopContinue);
} while (loopContinue != 'n');
}
void strFilter(char a[], char b[], char c){
int i = 0;
int n = 0;
while (n < 20) {
for (i = 0; i < 40; i++) {
if (a[i] == b[n]){
a[i] = c;
}
}
i = 0;
n++;
}
puts(a);
}
Thank you.
first of all please try to make your code a little easier to read, and I'm not talking about indenting but about its flow.
Also, your example output seems to work fine since there was nothing to change in any string here...?
There are a few things you should keep in mind when coding :
give your variables and functions explicit names, espcecially if you are going to have someone read your code at some point
try to keep the flow of your code simple by making small functions when you have a specifig task to execute (get the user's input, generate a random string, etc.) as opposed to just writing most of it in imbricated loops
You could also have a look at scanf (man scanf) to get the user's input
Try allocating a buffer when you get the user's input instead of having a static one that may not be of the right size
It's very easy to write some pseudo-code and then translate it into C :
WHILE someCondition
Generate a random string
Get a string from the user
Get a character from the user
Find and replace
END
Here is an example of how you could have organised your code (don't use it though - no frees, no getting the user's input, etc.) :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* generateString(void)
{
return "AEIOUYAEIOUY"; // In your implementation, this is where you'd generate the random string
}
char* getStringInput(void)
{
return "HELLO"; // In your implementation, this is where you'd get the user's string
}
char getCharInput(void)
{
return '*'; // In your implementation, this is where you'd get the user's character
}
char* findAndReplace(char* randomString, char* userString, char userChar)
{
int l1;
int l2;
int i;
int j;
char* output;
l1 = strlen(randomString);
l2 = strlen(userString);
output = (char*)malloc(sizeof(*output) * l1);
strcpy(output, randomString);
for (i = 0; i < l1; ++i)
{
for (j = 0; j < l2; ++j)
if (randomString[i] == userString[j])
output[i] = userChar;
}
return (output);
}
int main(int ac, char** av)
{
char* randomString;
char* userString;
char userChar;
char* outputString;
randomString = generateString();
userString = getStringInput();
userChar = getCharInput();
outputString = findAndReplace(randomString, userString, userChar);
printf("Result: %s\n", outputString);
// don't forget to free any allocated buffer
return (1);
}
How much debugging have you done? Try putting some printfs in your code to see what happens - when functions are called, what are your variable's values, etc.
Example :
void fillS1(char x[])
{
printf("-- entering fillS1, buffer value: %s\n", x);
for (int i = 0; i < 40; i++)
x[i] = 'A' + random() % 26;
x[40] = (char)0;
printf("-- leaving fillS1, buffer value: %s\n", x);
}
(be careful about what's in your buffer before you use printf)
This should tell you pretty quickly what's going wrong.
For example, try checking the value of "c" in strFilter when it's called, and have a second look at how you get the user's input.

C - Find most frequent element in char array

i'm developing a little function to display the most frequent character in a (char) array.
This is what I've accomplished so far, but I think i'm on the wrong way.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char test[10] = "ciaociaoci";
max_caratt(test, 10);
}
int max_caratt(char input[], int size)
{
int i;
char max[300];
max[0] = input[0];
for (i=0; i<size; i++)
{
if(strncmp(input,input[i],1) == 1)
{
printf("occourrence found");
max[i] = input[i];
}
}
}
Any help?
Actually, the correct code is this.
It's just a corrected version of IntermediateHacker's below snippet.
void main()
{
int array[255] = {0}; // initialize all elements to 0
char str[] = "thequickbrownfoxjumpedoverthelazydog";
int i, max, index;
for(i = 0; str[i] != 0; i++)
{
++array[str[i]];
}
// Find the letter that was used the most
max = array[0];
index = 0;
for(i = 0; str[i] != 0; i++)
{
if( array[str[i]] > max)
{
max = array[str[i]];
index = i;
}
}
printf("The max character is: %c \n", str[index]);
}
The easiest way to find the most common character is to create an int array of 255 and just increment the arraly element that corresponds to the character. For example: if the charcter is 'A', then increment the 'A'th element (if you look at any ascii table you will see that the letter 'A' has a decimal value of 65)
int array[255] = {0}; // initialize all elements to 0
char str[] = "The quick brown fox jumped over the lazy dog.";
int i, max, index;
// Now count all the letters in the sentence
for(i = 0; str[i] != 0; i++)
{
++array[str[i]];
}
// Find the letter that was used the most
max = array[0];
index = 0;
for(i = 0; str[i] != 0; i++)
{
if( array[i] > max)
{
max = array[i];
index = i;
}
}
printf("The max character is: %c \n", (char)index);
You're passing a (almost) string and a char to strncmp(). strncmp() takes two strings (and an integer). Your program shouldn't even compile!
Suggestion: increase the warning level of your compiler and mind the warnings.
You may want to look at strchr() ...
Assuming an input array of 0-127, the following should get you the most common character in a single pass through the string. Note, if you want to worry about negative numbers, shift everything up by +127 as needed...
char mostCommonChar(char *str) {
/* we are making the assumption that the string passed in has values
* between 0 and 127.
*/
int cnt[128], max = 0;
char *idx = str;
/* clear counts */
memset((void *)cnt, 0, sizeof(int) * 128);
/* collect info */
while(*idx) {
cnt[*idx]++;
if(cnt[*idx] > cnt[max]) {
max = *idx;
}
idx++;
}
/* we know the max */
return max;
}
If you don't need to preserve the input array, you could sort the input array first, then find the longest contiguous run of a single character. This approach is slower, but uses less space.
I made a working version using structs. It works fine, I guess, but I think there's a MUCH better way to write this algorithm.
#include <stdio.h>
#include <stdlib.h>
struct alphabet {
char letter;
int times;
};
typedef struct alphabet Alphabet;
void main() {
char string[300];
gets(string);
Alphabet Alph[300];
int i=0, j=0;
while (i<=strlen(string)) {
while(j<=300) {
if(string[i] != Alph[j].letter) {
Alph[i].letter = string[i];
Alph[i].times = 1;
}
else {
Alph[j].times++;
}
j++;
}
j=0;
i++;
}
int y,max=0;
char letter_max[0];
for (y=0; y<strlen(string); y++) {
printf("Letter: %c, Times: %d \n", Alph[y].letter, Alph[y].times);
if(Alph[y].times>max) {
max=Alph[y].times;
letter_max[0]=Alph[y].letter;
}
}
printf("\n\n\t\tMost frequent letter: %c - %d times \n\n", letter_max[0], max);
}
I saw you all creating big arrays and "complex" stuff so here I have easy and simple code xD
char most_used_char (char s[]) {
int i; //array's index
int v; //auxiliary index for counting characters
char c_aux; //auxiliary character
int sum = 0; //auxiliary character's occurrence
char c_max; //most used character
int max = 0; //most used character's occurrence
for (i = 0; s[i]; i++) {
c_aux = s[i];
for (v = 0; s[v]; v++)
if (c_aux == s[v]) sum++; /* responsible cycle for counting
character occurrence */
if (sum > max) { //checks if new character is the most used
max = sum;
c_max = c_aux;
}
sum = 0; /* reset counting variable so it can counts new
characters occurrence */
}
return c_max; //this is the most used character!
}

Resources