// Arup Guha
// 4/2/2026
// Trie Code written in class.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef struct TrieNode {
    struct TrieNode *children[26];
    int flag; // 1 if the string is in the trie, 0 otherwise
    int numwords; // the total # of words stored in this sub-trie.
} TrieNode;

TrieNode* makeEmptyNode(int isWord);
void insertWrapper(TrieNode* root, char* word);
void insertRec(TrieNode* curNode, char* word, int k, int n);
int numWordsWithPrefix(TrieNode* root, char* prefix);
int countWords(TrieNode* root, int freq[]);
void fillfreq(char* word, int* freq);
int maxNumPrefixWords(TrieNode* root);
int maxNumPreStore(TrieNode* root, int k, char* current, int curAns);

// To reconstruct prefix word.
int bestSoFar = 0;
char bestAns[100];

int main() {

    // Open dictionary file.
    FILE* ifp = fopen("dictionary.txt", "r");
    int n;
    fscanf(ifp, "%d", &n);

    // Set up root node.
    TrieNode* root = makeEmptyNode(0);

    // Read words in from a dictionary.
    for (int i=0; i<n; i++) {
        char word[100];
        fscanf(ifp, "%s", word);
        insertWrapper(root, word);
    }

    // Close it.

    fclose(ifp);

    // Basic test of this prefix question.
    printf("words that are prefixes = %d\n", maxNumPrefixWords(root));

    // Here we reconstruct the best word.
    char myword[100];
    printf("Solve with storing word = %d\n", maxNumPreStore(root, 0, myword, 0));
    printf("myword is %s %d\n", bestAns, bestSoFar);

    // Testing other stuff.
    while (1) {

        // Get word/prefix.
        printf("enter prefix\n");
        char word[100];
        scanf("%s", word);

        // # of prefixes.
        printf("# words with prefix is %d\n",numWordsWithPrefix(root, word) );

        // Scrabble question!
        int freq[26];
        fillfreq(word, freq);
        printf("num scrabble words = %d\n", countWords(root, freq));

        // Get out.
        if (strcmp(word, "no") == 0) break;
    }

    return 0;
}

// Returns an empty TrieNode.
TrieNode* makeEmptyNode(int isWord) {
    TrieNode* tmp = malloc(sizeof(TrieNode));
    tmp->flag = isWord;
    tmp->numwords = isWord;
    for (int i=0; i<26; i++)
        tmp->children[i] = NULL;
    return tmp;
}

void insertWrapper(TrieNode* root, char* word) {
    insertRec(root, word, 0, strlen(word));
}

void insertRec(TrieNode* curNode, char* word, int k, int n) {

    // There is one more word going down this path.
    curNode->numwords += 1;

    // We've processed the word.
    if (k == n) {
        curNode->flag = 1;
        return;
    }

    // Create an empty node.
    if (curNode->children[word[k]-'a'] == NULL)
        curNode->children[word[k]-'a'] = makeEmptyNode(0);

    // Recursively insert.
    insertRec(curNode->children[word[k]-'a'], word, k+1, n);
}

int numWordsWithPrefix(TrieNode* root, char* prefix) {

    int len = strlen(prefix);

    // Loop through the word.
    for (int i=0; i<len; i++) {

        // None!
        if (root->children[prefix[i]-'a'] == NULL)
            return 0;

        // Follow the next letter.
        root = root->children[prefix[i]-'a'];
    }

    // Here is the answer.
    return root->numwords;
}

int countWords(TrieNode* root, int freq[]) {

    if (root == NULL) return 0;

    // Count this as I could form it with my tiles.
    int res = root->flag;

    // Try the next letter.
    for (int i=0; i<26; i++) {

        // I don't have this letter to play.
        if (freq[i] == 0) continue;

        // Play this tile.
        freq[i]--;

        // Add all the words this could make us.
        res += countWords(root->children[i], freq);

        // Pick up the tile so we can play something different in this slot.
        freq[i]++;
    }

    return res;

}

// freq must be size 26.
void fillfreq(char* word, int* freq) {

    for (int i=0; i<26; i++)
        freq[i] = 0;
    int n = strlen(word);
    for (int i=0; i<n; i++)
        freq[word[i]-'a']++;
}

// Function I fixed with Justin.
int maxNumPreStore(TrieNode* root, int k, char* current, int curAns) {

    if (root == NULL) return 0;

    // Number of prefixes that end here.
    int res = root->flag;

    // Update our best answer and store the word if necessary.
    if (curAns+root->flag > bestSoFar) {
        bestSoFar = curAns+root->flag;
        strcpy(bestAns, current);
    }

    // Go down each branch.
    for (int i=0; i<26; i++) {

        // Store the letter.
        current[k] = (char)('a'+i);
        current[k+1] = '\0';

        // Get the answer.
        /*** My key issue was here: I had curAns+res, but res was changing, what I
             meant was curAns+root->flag.
        ***/
        int tmp = maxNumPreStore(root->children[i], k+1, current, curAns+root->flag);

        // If this is better, update our result.
        if (tmp + root->flag > res) {
            res = tmp + root->flag;
        }
    }

    // Return it.
    return res;
}

// Original version.
int maxNumPrefixWords(TrieNode* root) {

    if (root == NULL) return 0;

    // Nmber of prefixes that end here.
    int res = root->flag;

    for (int i=0; i<26; i++) {
        int tmp = maxNumPrefixWords(root->children[i]);
        if (tmp + root->flag > res)
            res = tmp + root->flag;
    }

    return res;
}
