Let's say I have a series of data that's in this form:
"SomethingIDontCareAbout : SomethingICareAbout"
where the part after the ":" can vary in length of course.
The goal here is only storing the "SomethingICareAbout" substring efficiently. I made this function but the problem is that I'm storing both substrings,so it seems like a waste of memory. Any help to reduce to the time/space complexity?
char** ExtractKey(char* S)
{
int n = strlen(S);
int count = 0, i = 0, j = 0;
for(i = 0; i < n; i++)
{
if(S[i] == ':')
break;
count++;
}
char** T = (char**)malloc(2 * sizeof(char*));
T[0] = (char*)malloc((count + 1) * sizeof(char));
T[1] = (char*)malloc((n - count) * sizeof(char));
for(i = 0; i < count; i++) // inefficient ? cus we won't need T[0] [j]
{
T[0][j] = S[i];
j++;
}
T[0][j+1] = '\0';
j = 0;
for(i = count + 1; i < n; i++)
{
T[1][j] = S[i];
j++;
}
T[1][j+1] = '\0';
return T;
}
There is no reason to invent a search for a character in a string, or a copy of a string.
If the input data will live long enough for you to use the "value" part, just return a pointer to the value:
char* ExtractKey(char* S)
{
return strchr(S, ':');
}
If it doesn't, or if you for some reason need a separate copy:
char* ExtractKey(char* S)
{
return strdup(strchr(S, ':'));
}
Honestly, this could be done efficiently if strtok() was used to split those strings. I have designed the following code that parses each string of a 2-D array with a common delimiter that is : here.
Now, let's take a look into the code (notice the comments):
#include <stdio.h>
#include <string.h>
#define MAX_LEN 128
int main(void) {
// The 2-D string
char str[][MAX_LEN] = {"SomethingElse : SomethingToCareAbout",
"Something2 : SomethingToCare2",
"Unnecessary : Necessary"};
int size = sizeof(str) / sizeof(str[0]);
// Applying Variable-Length Array (valid in C)
char store_cared_ones[size][MAX_LEN];
for (int i = 0; i < size; i++) {
// Declaring a temporary pointer variable to obtain the required
// substring from each string
char *sub_str = NULL;
sub_str = strtok(str[i], ": ");
sub_str = strtok(NULL, ": ");
// Copying the 'sub_str' into each array element of 'store_cared_ones'
strcpy(store_cared_ones[i], sub_str);
}
// Displaying each of 'store_cared_ones'
for (int i = 0; i < size; i++)
fprintf(stdout, "%s\n", store_cared_ones[i]);
return 0;
}
Finally, let's see what that code does:
rohanbari#genesis:~/stack$ ./a.out
SomethingToCareAbout
SomethingToCare2
Necessary
Related
Here is my code, The problem is that all the strings of allwords[i] change to the same string in the last of the code
My Code:
#include <stdio.h>
#include <string.h>
void print(int size, char *string)
{
char* word;
char** allwords;
word = malloc(size*sizeof(char));
allwords = malloc(sizeof(char*));
int c = -1;
for(int i = 0; i < size; i++)
{
if((*(string + i) == *(string))) /* if the start of the string is the same string of the original string */
{
for (int j = i; j < size; j++)
{
c++;
int k;
for (k = 0; k <= j - i; k++)
word[k] = string[k];
for(int s = k; s < strlen(word); s++) /* prevents unknown symbols */
word[s] = '\0';
allwords = realloc(allwords,(c+1)*sizeof(char*));
allwords[c] = malloc(strlen(word) * sizeof(char));
allwords[c] = word;
for(int f = 0; f <= c; f ++) /* Deletes all the similar strings, and keeps only one */
for(int t = f + 1; t < c; t++)
if(allwords[f] == allwords[t])
allwords[t] = '\0';
printf("%s\n",allwords[c]); /* prints the current string */
printf("%s\n",allwords[0]); /* To check if allwords[0] has changed or not during the code */
}
}
}
}
Input: (3 , "abc")
Output: a a ab ab abc abc
allwords[0] , allwords[1] , allwords[2] , all of them have "abc", but what I want is:
allwords[0] = "a" , allwords[1] = "ab" , allwords[2] = "abc"
I think that the problem is from malloc of allwords, but I don't know what I have to do to fix it, any suggestions ?.
Here is the answer from a friend
Replacing allwords[c] = word;
by:
strcpy(allwords[c] , word);
Reason: " Otherwise, you are copying pointers, and word changes along your flow – "
Is it possible to concatenate and add an array of pointers into one index of another array of pointers. I'm trying to take the strings inside my *token pointer and make it one string inside the first index of my commands pointer array, so on and so forth
cmd = strtok(str, " ");
while(n < 5 && (act_token = strtok(NULL, " ")))
{
token[n] = act_token;
n++;
}
token[n] = NULL;
/* Below is where I'm trying to add all the elements of the token array into one index of the comands array */
while( z < len ){
comands[b] = token[z];
z++;
}
b++;
}
To avoid O(n*n) complexity caused by looping a concatenation, in #zzxyz otherwise good answer, consider copying to the end of the accumulated destination.
char *concat_alloc(const char *token[], size_t n) {
size_t sum = 1;
for (size_t i = 0; i < n; i++) {
size_t len = strlen(token[i]);
sum += len;
if (sum < len) {
return NULL; // Too long
}
}
char *dest = malloc(sum);
if (dest) {
char *p = dest;
for (size_t i = 0; i < n; i++) {
size_t len = strlen(token[i]);
memcpy(p, token[i], len);
p += len; // advance to the end
}
*p = '\0';
}
return dest;
}
I feel your pain. String handling is very bad in C, and almost as bad in C++.
However, once you write the function, all you have to do is call it...
char *GetStringFromStringArray(const char**sourceStrings, size_t nCount)
{
char *destString = NULL;
size_t destLength = 1; //start with room for null-terminator
if (nCount == 0)
return destString;
for (size_t i = 0; i < nCount; i++)
destLength += strlen(sourceStrings[i]);
destString = (char*)malloc(destLength);
strcpy(destString, sourceStrings[0]);
for (size_t i = 1; i < nCount; i++)
strcat(destString, sourceStrings[i]);
return destString;
}
int main()
{
char *tokens[10] = { "bob", "jim", "hank" };
char *destStrings[2];
destStrings[0] = GetStringFromStringArray((const char**)tokens, 2);
destStrings[1] = GetStringFromStringArray((const char**)&tokens[1], 2);
free(destStrings[0]);
free(destStrings[1]);
}
The way I initialized tokens is not ok, by the way. Purely for easy example.
I have two strings:
char *str1 = "this is a test";
char *str2 = "ts bd a";
I'm trying to write a function that returns a new string with the same chars from the two string without duplicates (also ' ' is duplicate). eg.:
char *retStr = GetSameChars(str1, str2); //returns "ts a";
How can I do that?
What I'm tried:
char *GetSameChars(char str1[], char str2[]) {
int found = -1, i , j = 0, biggest, index = 0;
char *retArr, *star = '*';
int str1Len, str2Len, count = 0;
str1Len = strlen(str1);
str2Len = strlen(str2);
biggest = str1Len > str2Len ? str1Len : str2Len;
retArr = (char *)malloc(sizeof(char) * count);
for (i = 0; i < str1Len; i++) {
for (j = 0; j < str2Len; j++) {
if (str1[i] == str2[j] && found == -1) {
count++;
found = j;
} else
if (str2[j] == str2[found])
str2[j] = star; //Throw an exception
}
found = -1;
}
retArr = (char *)malloc(sizeof(char) * count);
j = 0;
for (i = 0; i < str2Len; i++)
if (str2[i] != '*')
retArr[j++] = str2[i];
for (i = 0; i < str2Len; i++)
printf("%c", retArr[i]);
}
When I tried the line str2[j] = star; I got an exception.
What is my mistake?
My recommendations would be: keep it simple; get to know the C standard library; write less, test more.
Some specific problems with your code: you pass the wrong variable to malloc(); you estimate the answer to fit in the size of the larger of the two strings but it will actually fit into the smaller of the two; you modify an argument string str2[j] = star -- you should be treating the arguments as readonly; you malloc() retArr twice unnecessarily, leaking the first one when you allocate the second; your algorithm simply doesn't work.
Although a lookup table, as others have suggested, would be more efficient, let's use the standard library routine strchr() to solve this problem:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *getSameChars(const char *string1, const char *string2) {
size_t string1_length = strlen(string1);
size_t string2_length = strlen(string2);
size_t shortest = string1_length < string2_length ? string1_length : string2_length;
char *common_pointer, *common = malloc(shortest + 1);
*(common_pointer = common) = '\0';
for (size_t i = 0; i < string1_length; i++) {
// character found in both input strings, but not yet in common string
if (strchr(string2, string1[i]) != NULL && strchr(common, string1[i]) == NULL) {
*common_pointer++ = string1[i];
*common_pointer = '\0';
}
}
return common;
}
int main() {
char *stringA = "this is a test";
char *stringB = "ts bd a";
char *result = getSameChars(stringA, stringB);
printf("%s\n", result);
free(result);
return(0);
}
Your code complains because you are trying to assign a pointer to a char, to get the value inside a pointer you need to use the * operator like so:
*star;
a good way to check if a letter have already appeared(if you want to use it on all of the ascii table then 128) is to use a lookup table. first you will need to declare an array the length of all letters in the alphabet like so:
char lut[26];
If it is a global variable then it will be set to 0, then all you need to do is go to the index of the char you got and mark it as 1, a simple if will later be able to determine if a letter has already appeard.
example:
lut[toupper(somechar) - 'A'] = 1;
In this example you set the char in the lookup table that is equivalent to the somechar variable as 1, marking it has already appeared.
hope this helps.
Suppose that we have a string "11222222345646". So how to print out subsequence 222222 in C.
I have a function here, but I think something incorrect. Can someone correct it for me?
int *longestsubstring(int a[], int n, int *length)
{
int location = 0;
length = 0;
int i, j;
for (i = 0, j = 0; i <= n-1, j < i; i++, j++)
{
if (a[i] != a[j])
{
if (i - j >= *length)
{
*length = i - j;
location = j;
}
j = i;
}
}
return &a[location];
}
Sorry,I don't really understand your question.
I just have a little code,and it can print the longest sub string,hope it can help.
/*breif : print the longest sub string*/
void printLongestSubString(const char * str,int length)
{
if(length <= 0)
return;
int i ;
int num1 = 0,num2 = 0;
int location = 0;
for(i = 0; i< length - 1; ++i)
{
if(str[i] == str[i+1])
++num2;//count the sub string ,may be not the longest,but we should try.
else
{
if(num2 >num1)//I use num1 store the sum longest of current sub string.
{ num1 = num2;location = i - num2;}
else
;//do nothing for short sub string.
num2 = 0;
}
}
for(i = location;str[i]== str[num1];++i)
printf("%c",str[i]);
printf("\n");
}
int main()
{
char * str = "1122222234566";
printLongestSubString(str,13);
return 0;
}
From your code it appears you want to return the longest sub-sequence (sub-string). Since I'm relearning C I thought I would give it a shot.
I've used strndup to extract the substring. I'm not sure how portable it is but I found an implementation if needed, just click on the link. It will allocate memory to store the new cstring so you have to remember to free the memory once finished with the substring. Following your argument list, the length of the sub-string is returned as the third argument of the extraction routine.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *extract_longest_subsequence(const char *str, size_t str_len, size_t *longest_len);
int main()
{
char str[] = "11222234555555564666666";
size_t substr_len = 0;
char *substr = extract_longest_subsequence(str, sizeof(str), &substr_len);
if (!substr)
{
printf("Error: NULL sub-string returned\n");
return 1;
}
printf("original string: %s, length: %zu\n", str, sizeof(str)-1);
printf("Longest sub-string: %s, length: %zu\n", substr, substr_len);
/* Have to remember to free the memory allocated by strndup */
free(substr);
return 0;
}
char *extract_longest_subsequence(const char *str, size_t str_len, size_t *longest_len)
{
if (str == NULL || str_len < 1 || longest_len == NULL)
return NULL;
size_t longest_start = 0;
*longest_len = 0;
size_t curr_len = 1;
size_t i = 0;
for (i = 1; i < str_len; ++i)
{
if (str[i-1] == str[i])
{
++curr_len;
}
else
{
if (curr_len > *longest_len)
{
longest_start = i - curr_len;
*longest_len = curr_len;
}
curr_len = 1;
}
}
/* strndup allocates memory for storing the substring */
return strndup(str + longest_start, *longest_len);
}
It looks like in your loop that j is supposed to be storing where the current "substring" starts, and i is the index of the character that you are currently looking at. In that case, you want to change
for (i = 0, j = 0; i <= n-1, j < i; i++, j++)
to
for (i = 0, j = 0; i <= n-1; i++)
That way, you are using i to store which character you're looking at, and the j = i line will "reset" which string of characters you are checking the length of.
Also, a few other things:
1) length = 0 should be *length = 0. You probably don't actually want to set the pointer to point to address 0x0.
2) That last line would return where your "largest substring" starts, but it doesn't truncate where the characters start to change (i.e. the resulting string isn't necessarily *length long). It can be intentional depending on use case, but figured I'd mention it in case it saves some grief.
How would i implement this?Im trying to compare a array of c strings to a single string and if there is no match append it to the 2d array.
char*mprt,uni[100][16];
mprt = &uni[0][0];
for (int s = 0;s <= 99;s++)
{
for (int z = 0;z <= 15;z++)
{
if (strcmp(mprt++, string1) != 0)
{
uni[s][z] = string1[z];
}
}
}
Ok ... from your comments I now get what you're trying to do. You'd want to make this into a function so you could feed words to it, but it should get you pointed in the right direction.
Note that you can use char[][], but this way your strings can be of any length because we dynamically allocate them when we put them in the list.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
/* space for 100 strings */
char **uni = calloc(100, sizeof(char*));
char **i;
/* Put one word in the list for test */
*uni = calloc(5, sizeof(char*));
strncpy(*uni, "this", 5);
/* here's the string we're going to search for */
char * str2 = "that";
/* go through the first dimension looking for the string
note we have to check that we don't exceed our list size */
for (i = uni; *i != NULL && i < uni+100; i++)
{
/* if we find it, break */
if (strcmp(*i,str2) == 0)
break;
}
/* if we didn't find the string, *i will be null
* or we will have hit the end of our first dimension */
if (i == uni + 100)
{
printf("No more space!\n");
}
else if (*i == NULL)
{
/* allocate space for our string */
*i = calloc(strlen(str2) + 1, sizeof(char));
/* copy our new string into the list */
strncpy(*i, str2, strlen(str2) + 1);
}
/* output to confirm it worked */
for (i = uni; *i != NULL && i < uni+100; i++)
printf("%s\n",*i);
}
For completeness, the char[][] version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char uni[100][16];
int i,j;
/* init our arrays */
for (i=0;i<100;i++)
for (j=0;j<16;j++)
uni[i][j] = '\0';
/* Put one word in the list for test */
strncpy(uni[0], "this",15);
/* here's the string we're going to search for */
char * str2 = "that";
/* go through the first dimension looking for the string */
for (i = 0; uni[i][0] != '\0' && i < 100; i++)
{
/* if we find it, break */
if (strcmp(uni[i],str2) == 0)
break;
}
/* if we didn't find the string, uni[i][0] will be '\0'
* or we will have hit the end of our first dimension */
if (i == 100)
{
printf("No more space!\n");
}
else if (uni[i][0] == '\0')
{
/* copy our new string into the array */
strncpy(uni[i], str2, 15);
}
/* output to confirm it worked */
for (i = 0; uni[i][0] != '\0' && i < 100; i++)
printf("%s\n",uni[i]);
}
Edit to explain C pointers and arrays from comments below:
In C, arrays degrade to pointers. This is actually really confusing when you first start.
If I have char myArray[10] and I want to pass that to a function that takes a char * argument, I can use either &myArray[0] or just myArray. When you leave off the index, it degrades to a pointer to the first element in the array.
In a multidimensional array like yours, &uni[5][0] == uni[5] - both are pointers to the first element in the second dimension at index 5 in the first. It degrades to char* pointed at the beginning of the 6th word in your list.
In your for loop, you need to copy the whole string to append it,
Replace the line by this,
strcpy(uni[s], string1[z]);
Considering string1[z] is an element of an array of char pointers.
Edit:
Not sure if this is what you're trying to do, but you'll end up with all elements set to string1
char string1[] = "String";
char uni[100][16] = {};
for (int s = 0; s < 100; s++)
{
if (strcmp(uni[s], string1) != 0)
{
strcpy(uni[s], string1);
}
}
Or this, without strcpy()
char string1[] = "String";
char uni[100][16] = {};
for (int s = 0; s < 100; s++)
{
for (int r = 0; r < sizeof(string1); r++)
{
uni[s][r] = string1[r];
}
}
to append to the end of the 2D array you need to use dynamic memory allocation
const int row_max = 100, col_max = 16;
char** uni = NULL;
char searchString[col_max] = "xyz";
int currentLength = 0;
uni = (char**) malloc (row_max * sizeof(char*)); //TODO:error handling code to be added
for (int row = 0; row < row_max; row++)
{
uni[row] = (char*)malloc(col_max * sizeof(char));//TODO:error handling code to be added
currentLength = row;
}
for (int row = 0; row < row_max; row++) //fill array uni with data here
{
uni[row] = "abc";
}
for (int row = 0; row < row_max; row++)
{
for (int col = 0; col < col_max; col++)
{
if (strcmp(&uni[row][col], searchString) != 0 )
{//string not found
uni = (char**)realloc(uni, (currentLength + 1) * sizeof(char*));//TODO:error handling code to be added
uni[currentLength + 1] = (char*)malloc(col_max);//TODO:error handling code to be added
currentLength++;
strcpy(uni[currentLength],searchString); //append at end of 2D array
goto stop;
}
}
}
stop:
for (int row = 0; row <= currentLength; row++)
free(uni[row]);
free(uni);
return 0;