Complicated solutions I did as a training. Two implementations controlled with a macro below.
First implementation loops through every character in the string, counts it's count in the first and second string and compares the values.
The second implementation allocates and creates a map of characters with count for each string and then compares these maps.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
// configuration
#define STRCHARSETCNTCMP_METHOD_FOREACH 0
#define STRCHARSETCNTCMP_METHOD_MAP 1
// eof configuration
//#define dbgln(fmt, ...) fprintf(stderr, "%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
#define dbgln(...) ((void)0)
/**
* STRing CHARacter SET CouNT CoMPare
* compare the count of set of characters in strings
* @param first string
* @param the other string
* @ret true if each character in s1 is used as many times in s2
*/
bool strcharsetcntcmp(const char s1[], const char s2[]);
// Count how many times the character is in the string
size_t strcharsetcntcmp_count(const char s[], char c)
{
assert(s != NULL);
size_t ret = 0;
while (*s != '\0') {
if (*s == c) {
++ret;
}
*s++;
}
return ret;
}
// foreach method implementation
bool strcharsetcntcmp_method_foreach(const char s1[], const char s2[])
{
const size_t s1len = strlen(s1);
const size_t s2len = strlen(s2);
if (s1len != s2len) {
return false;
}
for (size_t i = 0; i < s1len; ++i) {
const char c = s1[i];
const size_t cnt1 = strcharsetcntcmp_count(s1, c);
const size_t cnt2 = strcharsetcntcmp_count(s2, c);
// printf("'%s','%s' -> '%c' -> %zu %zu\n", s1, s2, c, cnt1, cnt2);
if (cnt1 != cnt2) {
return false;
}
}
return true;
}
// array of map elements
struct strcharsetcntcmp_map_s {
size_t cnt;
struct strcharsetcntcmp_map_cnt_s {
char c;
size_t cnt;
} *map;
};
// initialize empty map
void strcharsetcntcmp_map_init(struct strcharsetcntcmp_map_s *t)
{
assert(t != NULL);
dbgln("%p", t);
t->map = 0;
t->cnt = 0;
}
// free map memory
void strcharsetcntcmp_map_fini(struct strcharsetcntcmp_map_s *t)
{
assert(t != NULL);
dbgln("%p %p", t, t->map);
free(t->map);
t->map = 0;
t->cnt = 0;
}
// get the map element for character from map
struct strcharsetcntcmp_map_cnt_s *strcharsetcntcmp_map_get(const struct strcharsetcntcmp_map_s *t, char c)
{
assert(t != NULL);
for (size_t i = 0; i < t->cnt; ++i) {
if (t->map[i].c == c) {
return &t->map[i];
}
}
return NULL;
}
// check if the count for character c was already added into the map
bool strcharsetcntcmp_map_exists(const struct strcharsetcntcmp_map_s *t, char c)
{
return strcharsetcntcmp_map_get(t, c) != NULL;
}
// map element into map, without checking if it exists (only assertion)
int strcharsetcntcmp_map_add(struct strcharsetcntcmp_map_s *t, char c, size_t cnt)
{
assert(t != NULL);
assert(strcharsetcntcmp_map_exists(t, c) == false);
dbgln("%p %p %zu %c %zu", t, t->map, t->cnt, c, cnt);
void *pnt = realloc(t->map, sizeof(t->map[0]) * (t->cnt + 1));
if (pnt == NULL) {
return -errno;
}
t->map = pnt;
t->map[t->cnt].c = c;
t->map[t->cnt].cnt = cnt;
t->cnt++;
return 0;
}
// create map from string, map needs to be initialized by init and needs to be freed with fini
int strcharsetcntcmp_map_parsestring(struct strcharsetcntcmp_map_s *t, const char s[])
{
assert(t != NULL);
assert(s != NULL);
int ret = 0;
while (*s != '\0') {
const char c = *s;
if (!strcharsetcntcmp_map_exists(t, c)) {
const size_t cnt = strcharsetcntcmp_count(s, c);
ret = strcharsetcntcmp_map_add(t, c, cnt);
if (ret != 0) {
break;
}
}
++s;
}
return ret;
}
// compare two maps if they have same sets of characters and counts
bool strcharsetcntcmp_cmp(const struct strcharsetcntcmp_map_s *t, const struct strcharsetcntcmp_map_s *o)
{
assert(t != NULL);
assert(o != NULL);
if (t->cnt != o->cnt) {
return false;
}
for (size_t i = 0; i < t->cnt; ++i) {
const char c = t->map[i].c;
const size_t t_cnt = t->map[i].cnt;
struct strcharsetcntcmp_map_cnt_s *o_map_cnt = strcharsetcntcmp_map_get(o, c);
if (o_map_cnt == NULL) {
dbgln("%p(%zu) %p(%zu) %c not found", t, t->cnt, o, o->cnt, c);
return false;
}
const size_t o_cnt = o_map_cnt->cnt;
if (t_cnt != o_cnt) {
dbgln("%p(%zu) %p(%zu) %c %zu != %zu", t, t->cnt, o, o->cnt, c, t_cnt, o_cnt);
return false;
}
dbgln("%p(%zu) %p(%zu) %c %zu", t, t->cnt, o, o->cnt, c, t_cnt);
}
return true;
}
// map method implementation
bool strcharsetcntcmp_method_map(const char s1[], const char s2[])
{
struct strcharsetcntcmp_map_s map1;
strcharsetcntcmp_map_init(&map1);
if (strcharsetcntcmp_map_parsestring(&map1, s1) != 0) {
abort(); // <insert good error handler here>
}
struct strcharsetcntcmp_map_s map2;
strcharsetcntcmp_map_init(&map2);
if (strcharsetcntcmp_map_parsestring(&map2, s2) != 0) {
abort(); // <insert good error handler here>
}
const bool ret = strcharsetcntcmp_cmp(&map1, &map2);
strcharsetcntcmp_map_fini(&map1);
strcharsetcntcmp_map_fini(&map2);
return ret;
}
bool strcharsetcntcmp(const char s1[], const char s2[])
{
assert(s1 != NULL);
assert(s2 != NULL);
#if STRCHARSETCNTCMP_METHOD_FOREACH
return strcharsetcntcmp_method_foreach(s1, s2);
#elif STRCHARSETCNTCMP_METHOD_MAP
return strcharsetcntcmp_method_map(s1, s2);
#endif
}
// unittests. Should return 0
int strcharsetcntcmp_unittest(void)
{
struct {
const char *str1;
const char *str2;
bool eq;
} const tests[] = {
{ "", "", true, },
{ "a", "b", false, },
{ "abc", "bca", true, },
{ "aab", "abb", false, },
{ "aabbbc", "cbabab", true, },
{ "123456789012345678901234567890qwertyuiopqwertyuiopasdfghjklasdfghjklzxcvbnmzxcvbnm,./;", "123456789012345678901234567890qwertyuiopqwertyuiopasdfghjklasdfghjklzxcvbnmzxcvbnm,./;", true },
{ "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", true },
{ "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900", false },
};
int ret = 0;
for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); ++i) {
const bool is = strcharsetcntcmp(tests[i].str1, tests[i].str2);
if (is != tests[i].eq) {
fprintf(stderr,
"Error: strings '%s' and '%s' returned %d should be %d\n",
tests[i].str1, tests[i].str2, is, tests[i].eq);
ret = -1;
}
}
return ret;
}
int main()
{
return strcharsetcntcmp_unittest();
}