Algo problem in C
Summary of problem:
-
Adding a password → store all possible substrings of the password.
-
Querying a string → count how many other passwords (not equal to the query string itself) have this string as a substring.
Problematic behavior in your implementation:
Bug location is in how substrings are hashed and stored.
Your current implementation:
-
At each
add(password)
call:-
Creates all possible substrings.
-
Stores an entry for each substring in a hash table
substrings
with the substring assubstr
, and the original password asogPassword
.
-
In query(password, password)
:
-
Hash the query password and check the linked list at that hash bucket.
-
For each matching
substr
equal to query string and differentogPassword
, increment counter.
The issue:
You hash each substring individually by value. But — different substrings from different passwords could hash to the same bucket, and your lookup only scans the linked list at a single hash bucket for a given query string's hash value.
That means:
-
You’ll only see entries whose hashed value exactly matches the hash of the full query string.
-
But many substrings of other passwords containing the query string could hash to other hash buckets.
→ The hashing strategy here breaks correctness because you can't rely on the hash bucket of the query string to contain all substrings equal to it.
Two identical strings will always hash to the same value, yes — but you would have had to insert that exact string as a substring somewhere to be in that bucket.
In short:
The current design can only ever find substrings equal to the query string if it was itself inserted as a substring during a password’s add operation.
This works, but it's highly inefficient: for querying, you'd need to find all passwords whose substrings contain the query string — and with only a hash lookup by exact string length, you miss all other potential locations.
Correct, scalable design should either:
-
Use a trie (prefix tree) to store all substrings and allow efficient substring matching during queries.
-
Brute-force: Store a list of all passwords, and on query, for each password (except the query one itself), check
strstr()
if query string is a substring.-
It’s acceptable here since passwords are short (≤10 characters) and per problem constraints.
-
Minimal working fix:
If sticking to your current hash-table based approach:
-
During
query(password, password)
— instead of hashing the query and looking only at that bucket, you'd need to:-
Iterate over all hash buckets.
-
Check each
entry
in all linked lists. -
For entries where
substr == password
andogPassword != password
, increment the counter.
-
Why? Because identical substrings could hash to different buckets unless the hash function is perfectly consistent (but that's unnecessary overhead for this).
Option 2: Trie-Based Substring Indexing
Idea:
-
Each node represents a character.
-
Paths down the trie form substrings.
-
Each node tracks which passwords inserted this substring (as a linked list or set).
-
On query: follow the trie path for the query string. If path exists — count number of unique passwords associated with this substring (excluding the query password itself).
#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_PASSWORD_LEN 12 #define ALPHABET_SIZE 26 #define MAX_USERS 131072 typedef struct PasswordNode { char* password; struct PasswordNode* next; } PasswordNode; typedef struct TrieNode { struct TrieNode* children[ALPHABET_SIZE]; PasswordNode* passwords; } TrieNode; TrieNode* createTrieNode() { TrieNode* node = (TrieNode*)malloc(sizeof(TrieNode)); for (int i = 0; i < ALPHABET_SIZE; i++) node->children[i] = NULL; node->passwords = NULL; return node; } TrieNode* root; char* passwords[MAX_USERS]; int passwordCount = 0; void addPasswordToNode(TrieNode* node, const char* password) { PasswordNode* p = node->passwords; while (p != NULL) { if (strcmp(p->password, password) == 0) return; // already recorded p = p->next; } PasswordNode* newPasswordNode = (PasswordNode*)malloc(sizeof(PasswordNode)); newPasswordNode->password = (char*)malloc(strlen(password) + 1); strcpy(newPasswordNode->password, password); newPasswordNode->next = node->passwords; node->passwords = newPasswordNode; } void add(const char* password) { passwords[passwordCount] = malloc(strlen(password) + 1); strcpy(passwords[passwordCount], password); passwordCount++; int len = strlen(password); for (int start = 0; start < len; start++) { TrieNode* curr = root; for (int i = start; i < len; i++) { int idx = password[i] - 'a'; if (!curr->children[idx]) curr->children[idx] = createTrieNode(); curr = curr->children[idx]; addPasswordToNode(curr, password); } } } int query(const char* queryString) { TrieNode* curr = root; int len = strlen(queryString); for (int i = 0; i < len; i++) { int idx = queryString[i] - 'a'; if (!curr->children[idx]) return 0; curr = curr->children[idx]; } int counter = 0; PasswordNode* p = curr->passwords; while (p != NULL) { if (strcmp(p->password, queryString) != 0) counter++; p = p->next; } return counter; } void freeTrie(TrieNode* node) { if (!node) return; for (int i = 0; i < ALPHABET_SIZE; i++) freeTrie(node->children[i]); PasswordNode* p = node->passwords; while (p) { PasswordNode* temp = p; p = p->next; free(temp->password); free(temp); } free(node); } int main() { int operations; scanf("%d", &operations); int opcode; char password[MAX_PASSWORD_LEN]; root = createTrieNode(); for (int i = 0; i < operations; i++) { scanf("%d %s", &opcode, password); if (opcode == 1) { add(password); } else if (opcode == 2) { printf("%d\n", query(password)); } } // clean up for (int i = 0; i < passwordCount; i++) free(passwords[i]); freeTrie(root); return 0; }
Do not underestimate AI, buddy. I have many LeetCode submissions where a silly bug had me banging my head debugging for hours, but a short copy and paste into AI immediately spits out the line where the bug occurs. Use AI like StackOverflow. This is pretty much what AI is, just a better version of online googling. it wont work 100% usually, that's true but it is quite close. After some manual tweaks, it can be adjusted to work correctly, saving much time.
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now