Open In App

Longest Valid Word with All Prefixes

Last Updated : 03 Jul, 2025
Comments
Improve
Suggest changes
2 Likes
Like
Report

Given an array of strings arr[], find the longest string such that all of its prefixes are also present in arr[]. If there are multiple such strings with the same maximum length, return the lexicographically smallest one.

Note: A prefix of a string is any substring starting from the first character, including the string itself.

Examples:

Input: arr[] = ["ab", "a", "abc", "abd"]
Output: abc
Explanation: Each of "abc" and "abd" has all its prefixes in the array. ("abc" → prefixes: "a", "ab", "abc " and "abd" → prefixes: "a", "ab", "abd"). Since both satisfy the condition and are of equal length, we return the lexicographically smaller one, which is "abc".

Input: arr[] = ["p", "pr", "pro", "probl", "problem", "pros", "process", "processor"]
Output: "pros"
Explanation: "pros" is the longest word with all prefixes present in the array(i.e., "p", "pr", "pro" , "pros")

[Naive Approach] Using Sorting and Binary Search

The idea is to sort the words lexicographically, then iterate through each word and check if all of its prefixes (including itself) exist in the list using binary search. The longest valid word is tracked, and in case of a tie, the lexicographically smaller one is chosen.

C++
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

string longestString(vector<string>& arr) {
    
    // sort the arr for Binary Search to work correctly
    sort(arr.begin(), arr.end());
    
    string result;

    // check each word
    for (string& word : arr) {
        bool isValid = true;
        string prefix;

        // check all prefixes of the word
        for (char ch : word) {
            prefix += ch;
            
            // if the prefix is not in the array
            if (!binary_search(arr.begin(), arr.end(), prefix)) {
                isValid = false;
                break;
            }
        }

        // update result if current word is valid 
        // and better than current result
        if (isValid && 
            (word.size() > result.size() || 
            (word.size() == result.size() && word < result))) {
            result = word;
        }
    }

    return result;
}

int main() {
    vector<string> arr = {"ab", "a", "abc", "abd"};
    cout << longestString(arr) << endl;
    return 0;
}
Java
import java.util.Arrays;

class GfG {

    public static String longestString(String[] arr) {
        
        // sort the arr for Binary Search to work correctly
        Arrays.sort(arr);
        
        String result = "";

        // check each word
        for (String word : arr) {
            boolean isValid = true;
            String prefix = "";

            // check all prefixes of the word
            for (char ch : word.toCharArray()) {
                prefix += ch;

                // if the prefix is not in the array
                if (Arrays.binarySearch(arr, prefix) < 0) {
                    isValid = false;
                    break;
                }
            }

            // update result if current word is valid 
            // and better than current result
            if (isValid && 
                (word.length() > result.length() || 
                (word.length() == result.length() &&
                            word.compareTo(result) < 0))) {
                result = word;
            }
        }

        return result;
    }

    public static void main(String[] args) {
        String[] arr = {"ab", "a", "abc", "abd"};
        System.out.println(longestString(arr));
    }
}
Python
def longestString(arr):
    
    # sort the arr for Binary Search to work correctly
    arr.sort()
    
    result = ""

    # check each word
    for word in arr:
        isValid = True
        prefix = ""

        # check all prefixes of the word
        for ch in word:
            prefix += ch

            # if the prefix is not in the array
            if prefix not in arr:
                isValid = False
                break

        # update result if current word is valid 
        # and better than current result
        if isValid and (
            len(word) > len(result) or 
            (len(word) == len(result) and word < result)):
            result = word

    return result


if __name__ == "__main__":
    arr = ["ab", "a", "abc", "abd"]
    print(longestString(arr))
C#
using System;
using System.Collections.Generic;

class GfG {

    public static string longestString(string[] arr) {
        
        // sort the arr for Binary Search to work correctly
        Array.Sort(arr);
        
        string result = "";

        // check each word
        foreach (string word in arr) {
            bool isValid = true;
            string prefix = "";

            // check all prefixes of the word
            foreach (char ch in word) {
                prefix += ch;

                // if the prefix is not in the array
                if (Array.BinarySearch(arr, prefix) < 0) {
                    isValid = false;
                    break;
                }
            }

            // update result if current word is valid 
            // and better than current result
            if (isValid &&
                (word.Length > result.Length || 
                (word.Length == result.Length &&
                            string.Compare(word, result) < 0))) {
                result = word;
            }
        }

        return result;
    }

    public static void Main(string[] args) {
        string[] arr = { "ab", "a", "abc", "abd" };
        Console.WriteLine(longestString(arr));
    }
}
JavaScript
function longestString(arr) {

    // sort the arr for Binary Search to work correctly
    arr.sort();

    let result = "";

    // check each word
    for (let word of arr) {
        let isValid = true;
        let prefix = "";

        // check all prefixes of the word
        for (let ch of word) {
            prefix += ch;

            // if the prefix is not in the array
            if (!arr.includes(prefix)) {
                isValid = false;
                break;
            }
        }

        // update result if current word is valid 
        // and better than current result
        if (isValid &&
            (word.length > result.length ||
            (word.length === result.length && word < result))) {
            result = word;
        }
    }

    return result;
}

// Driver Code
const arr = ["ab", "a", "abc", "abd"];
console.log(longestString(arr));

Output
abc

Time Complexity: O(n*k2*log n) for each word, of length up to k, generating k prefixes, with each requiring a binary search that cost O(k * log n) due to string comparison.
Auxiliary Space: O(1) as no extra data structure are used apart from a few variable.

[Better Approach] Rabin-Karp Based Prefix Validation

This approach uses the Rabin-Karp rolling hash technique with double hashing to uniquely represent each word and its prefixes using a pair of hash values, reducing collision risk. All word hashes are stored in a set. For each word, we incrementally compute prefix hashes using Rabin-Karp and check if all prefixes exist in the set. The longest valid word (or lexicographically smallest among equals) is returned.

Note: We avoid using only one hash because it increases the risk of hash collisions, which can cause incorrect prefix validation. Double hashing greatly reduces this risk by ensuring two independent hash checks.

C++
#include <iostream>
#include <vector>
#include <set>
#include <string>
#include <utility>

using namespace std;

const int base1 = 31;
const int mod1 = 1e9 + 7;
const int base2 = 37;
const int mod2 = 1e9 + 9;

// function to compute pair hash
vector<int> computeHash(string& s) {
    int h1 = 0, h2 = 0;
    int p1 = 1, p2 = 1;

    for (char ch : s) {
        int val = ch - 'a' + 1;
        h1 = (h1 + val * 1LL * p1) % mod1;
        h2 = (h2 + val * 1LL * p2) % mod2;

        p1 = (p1 * 1LL * base1) % mod1;
        p2 = (p2 * 1LL * base2) % mod2;
    }

    return { (int)h1, (int)h2 };
}

// check if all prefixes of word exist in hash set
bool allPrefixExist(string& word, set<vector<int>>& hashSet) {
    int h1 = 0, h2 = 0;
    int p1 = 1, p2 = 1;

    for (int i = 0; i < word.size(); ++i) {
        int val = word[i] - 'a' + 1;
        h1 = (h1 + val * 1LL * p1) % mod1;
        h2 = (h2 + val * 1LL * p2) % mod2;

        if (hashSet.find({ (int)h1, (int)h2 }) == hashSet.end()) {
            return false;
        }

        p1 = (p1 * 1LL * base1) % mod1;
        p2 = (p2 * 1LL * base2) % mod2;
    }

    return true;
}

// function to get the longest word whose all prefixes exist
string longestString(vector<string>& arr) {
    
    set<vector<int>> hashSet;

    for (string& word : arr) {
        hashSet.insert(computeHash(word));
    }

    string result = "";
    
    // check for each word
    for (string& word : arr) {
        if (allPrefixExist(word, hashSet)) {
            
            // update result if word is longer or 
            // lexicographically smaller
            if (word.length() > result.length() || 
                (word.length() == result.length() && word < result)) {
                result = word;
            }
        }
    }

    return result;
}


int main() {
    
    vector<string> arr = {
        "ab", "a", "abc", "abd"
    };
    cout << longestString(arr) << endl;

    return 0;
}
Java
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;


class GfG {

    static final int base1 = 31;
    static final int mod1 = 1000000007;
    static final int base2 = 37;
    static final int mod2 = 1000000009;

    // function to compute pair hash
    static ArrayList<Integer> computeHash(String s) {
        long h1 = 0, h2 = 0;
        long p1 = 1, p2 = 1;

        for (char ch : s.toCharArray()) {
            int val = ch - 'a' + 1;
            h1 = (h1 + val * p1) % mod1;
            h2 = (h2 + val * p2) % mod2;

            p1 = (p1 * base1) % mod1;
            p2 = (p2 * base2) % mod2;
        }

        ArrayList<Integer> hash = new ArrayList<>();
        hash.add((int) h1);
        hash.add((int) h2);
        return hash;
    }

    // check if all prefixes of word exist in hash set
    static boolean allPrefixExist(String word, Set<ArrayList<Integer>> hashSet) {
        long h1 = 0, h2 = 0;
        long p1 = 1, p2 = 1;

        for (int i = 0; i < word.length(); i++) {
            int val = word.charAt(i) - 'a' + 1;
            h1 = (h1 + val * p1) % mod1;
            h2 = (h2 + val * p2) % mod2;

            ArrayList<Integer> prefixHash = new ArrayList<>();
            prefixHash.add((int) h1);
            prefixHash.add((int) h2);

            if (!hashSet.contains(prefixHash)) {
                return false;
            }

            p1 = (p1 * base1) % mod1;
            p2 = (p2 * base2) % mod2;
        }

        return true;
    }

    // function to get the longest word whose all prefixes exist
    static String longestString(String[] arr) {
        Set<ArrayList<Integer>> hashSet = new HashSet<>();

        for (String word : arr) {
            hashSet.add(computeHash(word));
        }

        String result = "";

        // check for each word
        for (String word : arr) {
            if (allPrefixExist(word, hashSet)) {
                // update result if word is longer or 
                // lexicographically smaller
                if (word.length() > result.length() ||
                        (word.length() == result.length() && word.compareTo(result) < 0)) {
                    result = word;
                }
            }
        }

        return result;
    }

    public static void main(String[] args) {
        String[] arr = {"ab", "a", "abc", "abd"};
        System.out.println(longestString(arr));
    }
}
Python
base1 = 31
mod1 = 10**9 + 7
base2 = 37
mod2 = 10**9 + 9

# function to compute pair hash
def computeHash(s):
    h1 = 0
    h2 = 0
    p1 = 1
    p2 = 1

    for ch in s:
        val = ord(ch) - ord('a') + 1
        h1 = (h1 + val * p1) % mod1
        h2 = (h2 + val * p2) % mod2

        p1 = (p1 * base1) % mod1
        p2 = (p2 * base2) % mod2

    return (h1, h2)

# check if all prefixes of word exist in hash set
def allPrefixExist(word, hashSet):
    h1 = 0
    h2 = 0
    p1 = 1
    p2 = 1

    for i in range(len(word)):
        val = ord(word[i]) - ord('a') + 1
        h1 = (h1 + val * p1) % mod1
        h2 = (h2 + val * p2) % mod2

        if (h1, h2) not in hashSet:
            return False

        p1 = (p1 * base1) % mod1
        p2 = (p2 * base2) % mod2

    return True

# function to get the longest word whose all prefixes exist
def longestString(arr):
    hashSet = set()
    
    for word in arr:
        hashSet.add(computeHash(word))

    result = ""

    # check for each word
    for word in arr:
        if allPrefixExist(word, hashSet):
            # update result if word is longer or 
            # lexicographically smaller
            if len(word) > len(result) or (len(word) == len(result) and word < result):
                result = word

    return result


if __name__ == "__main__":
    arr = ["ab", "a", "abc", "abd"]
    print(longestString(arr))
C#
using System;
using System.Collections.Generic;

class GfG {
    static int base1 = 31;
    static int mod1 = 1000000007;
    static int base2 = 37;
    static int mod2 = 1000000009;

    // function to compute pair hash
    static int[] computeHash(string s) {
        long h1 = 0, h2 = 0;
        long p1 = 1, p2 = 1;

        foreach (char ch in s) {
            int val = ch - 'a' + 1;
            h1 = (h1 + val * p1) % mod1;
            h2 = (h2 + val * p2) % mod2;

            p1 = (p1 * base1) % mod1;
            p2 = (p2 * base2) % mod2;
        }

        return new int[] { (int)h1, (int)h2 };
    }

    // check if all prefixes of word exist in hash set
    static bool allPrefixExist(string word, HashSet<string> hashSet) {
        long h1 = 0, h2 = 0;
        long p1 = 1, p2 = 1;

        for (int i = 0; i < word.Length; i++) {
            int val = word[i] - 'a' + 1;
            h1 = (h1 + val * p1) % mod1;
            h2 = (h2 + val * p2) % mod2;

            string key = h1 + "," + h2;
            if (!hashSet.Contains(key)) {
                return false;
            }

            p1 = (p1 * base1) % mod1;
            p2 = (p2 * base2) % mod2;
        }

        return true;
    }

    // function to get the longest word whose all prefixes exist
    static string longestString(string[] arr) {
        HashSet<string> hashSet = new HashSet<string>();

        foreach (string word in arr) {
            int[] hash = computeHash(word);
            hashSet.Add(hash[0] + "," + hash[1]);
        }

        string result = "";

        // check for each word
        foreach (string word in arr) {
            if (allPrefixExist(word, hashSet)) {
                // update result if word is longer or 
                // lexicographically smaller
                if (word.Length > result.Length ||
                    (word.Length == result.Length && string.Compare(word, result) < 0)) {
                    result = word;
                }
            }
        }

        return result;
    }

    static void Main(string[] args) {
        string[] arr = { "ab", "a", "abc", "abd" };
        Console.WriteLine(longestString(arr));
    }
}
JavaScript
const base1 = 31;
const mod1 = 1e9 + 7;
const base2 = 37;
const mod2 = 1e9 + 9;

// function to compute pair hash
function computeHash(s) {
    let h1 = 0, h2 = 0;
    let p1 = 1, p2 = 1;

    for (let i = 0; i < s.length; i++) {
        let val = s.charCodeAt(i) - 'a'.charCodeAt(0) + 1;
        h1 = (h1 + val * p1) % mod1;
        h2 = (h2 + val * p2) % mod2;

        p1 = (p1 * base1) % mod1;
        p2 = (p2 * base2) % mod2;
    }

    return [Math.floor(h1), Math.floor(h2)];
}

// check if all prefixes of word exist in hash set
function allPrefixExist(word, hashSet) {
    let h1 = 0, h2 = 0;
    let p1 = 1, p2 = 1;

    for (let i = 0; i < word.length; i++) {
        let val = word.charCodeAt(i) - 'a'.charCodeAt(0) + 1;
        h1 = (h1 + val * p1) % mod1;
        h2 = (h2 + val * p2) % mod2;

        let key = `${h1},${h2}`;
        if (!hashSet.has(key)) {
            return false;
        }

        p1 = (p1 * base1) % mod1;
        p2 = (p2 * base2) % mod2;
    }

    return true;
}

// function to get the longest word whose all prefixes exist
function longestString(arr) {
    let hashSet = new Set();

    for (let word of arr) {
        let [h1, h2] = computeHash(word);
        hashSet.add(`${h1},${h2}`);
    }

    let result = "";

    // check for each word
    for (let word of arr) {
        if (allPrefixExist(word, hashSet)) {
            // update result if word is longer or 
            // lexicographically smaller
            if (word.length > result.length || 
               (word.length === result.length && word < result)) {
                result = word;
            }
        }
    }

    return result;
}

// Driver Code
let arr = ["ab", "a", "abc", "abd"];
console.log(longestString(arr));

Output
abc

Time Complexity: O(n * k * log n) - for each word checking all k prefixes in the set (log n per prefix).
Auxiliary Space: O(n) - used to store the hash ( pair of integers ) of each word.

[Expected Approach] Trie-Based Prefix Validation

The idea is to insert all words into a Trie, marking the end of each word. Then, for each word, we check whether all its prefixes also exist as complete words in the Trie. If a word passes this check, we consider it as a candidate for the result. Among all valid candidates, we select the longest one, preferring lexicographically smaller words in case of a tie.

Algorithm:

  • Define a TrieNode with 26 child pointers and a boolean isEnd.
  • Insert every word from the array into the Trie.
  • While inserting, create missing child nodes and mark isEnd = true at the last character.
  • For each word, verify that every prefix is present in the Trie and has isEnd = true.
  • Keep the longest word that passes this check; on ties choose the lexicographically smallest.
C++
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

// trie node definition
struct TrieNode {
    TrieNode* children[26];
    bool isEnd;

    TrieNode() {
        isEnd = false;
        fill(begin(children), end(children), nullptr);
    }
};

class Trie {
public:
    TrieNode* root;

    Trie() {
        root = new TrieNode();
    }

    // insert a word into the trie
    void insert(string& word) {
        TrieNode* node = root;
        for (char ch : word) {
            int idx = ch - 'a';
            if (!node->children[idx]) {
                node->children[idx] = new TrieNode();
            }
            node = node->children[idx];
        }
        // marking the end of the word
        node->isEnd = true;
    }

    // check if all prefixes of the word exist in the trie
    bool allPrefixesExist(string& word) {
        TrieNode* node = root;
        for (char ch : word) {
            int idx = ch - 'a';
            node = node->children[idx];

            // if the prefix is missing or not marked as end
            if (!node || !node->isEnd) {
                return false;
            }
        }
        return true;
    }
};

// Function to find the longest word whose 
// all prefixes exist in the list
string longestString(vector<string>& arr) {
    Trie trie;
    
    // insert all words into the trie
    for (string& word : arr) {
        trie.insert(word);
    }

    string result = "";

    // check each word
    for (string& word : arr) {
        
        // if all prefixes exist
        if (trie.allPrefixesExist(word)) {
            
            // update result if word is longer or 
            // lexicographically smaller
            if (word.length() > result.length() ||
                (word.length() == result.length() && word < result)) {
                result = word;
            }
        }
    }

    return result;
}

int main() {
    vector<string> arr = {"ab", "a", "abc", "abd"};
    cout << longestString(arr) << endl;
    return 0;
}
Java
import java.util.Arrays;

class TrieNode {
    TrieNode[] children = new TrieNode[26];
    boolean isEnd = false;

    TrieNode() {
        Arrays.fill(children, null);
    }
}

class Trie {
    TrieNode root;

    Trie() {
        root = new TrieNode();
    }

    // insert a word into the trie
    void insert(String word) {
        TrieNode node = root;
        for (char ch : word.toCharArray()) {
            int idx = ch - 'a';
            if (node.children[idx] == null) {
                node.children[idx] = new TrieNode();
            }
            node = node.children[idx];
        }
        // marking the end of the word
        node.isEnd = true;
    }

    // check if all prefixes of the word exist in the trie
    boolean allPrefixesExist(String word) {
        TrieNode node = root;
        for (char ch : word.toCharArray()) {
            int idx = ch - 'a';
            node = node.children[idx];

            // if the prefix is missing or not marked as end
            if (node == null || !node.isEnd) {
                return false;
            }
        }
        return true;
    }
}

public class GfG {
    
    // Function to find the longest word whose 
    // all prefixes exist in the list
    public static String longestString(String[] arr) {
        Trie trie = new Trie();

        // insert all words into the trie
        for (String word : arr) {
            trie.insert(word);
        }

        String result = "";

        // sort the arr to ensure lexicographical order
        Arrays.sort(arr);

        // check each word
        for (String word : arr) {

            // if all prefixes exist
            if (trie.allPrefixesExist(word)) {

                // update result if word is longer or 
                // lexicographically smaller
                if (word.length() > result.length() ||
                        (word.length() == result.length() &&
                                    word.compareTo(result) < 0)) {
                    result = word;
                }
            }
        }

        return result;
    }

    public static void main(String[] args) {
        String[] arr = {"ab", "a", "abc", "abd"};
        System.out.println(longestString(arr));
    }
}
Python
# trie node definition
class TrieNode:
    def __init__(self):
        self.children = [None] * 26
        self.isEnd = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    # insert a word into the trie
    def insert(self, word):
        node = self.root
        for ch in word:
            idx = ord(ch) - ord('a')
            if not node.children[idx]:
                node.children[idx] = TrieNode()
            node = node.children[idx]
        # marking the end of the word
        node.isEnd = True

    # check if all prefixes of the word exist in the trie
    def allPrefixesExist(self, word):
        node = self.root
        for ch in word:
            idx = ord(ch) - ord('a')
            node = node.children[idx]

            # if the prefix is missing or not marked as end
            if not node or not node.isEnd:
                return False
        return True



# Function to find the longest word whose 
# all prefixes exist in the list
def longestString(words):
    trie = Trie()

    # insert all words into the trie
    for word in arr:
        trie.insert(word)

    result = ""

    # check each word
    for word in arr:

        # if all prefixes exist
        if trie.allPrefixesExist(word):

            # update result if word is longer or 
            # lexicographically smaller
            if len(word) > len(result) or (len(word) == len(result) \
                                        and word < result):
                result = word

    return result


if __name__ == "__main__":
    arr = ["ab", "a", "abc", "abd"]
    print(longestString(arr))
C#
using System;

class TrieNode {
    public TrieNode[] children = new TrieNode[26];
    public bool isEnd = false;

    public TrieNode() {
        for (int i = 0; i < 26; i++) {
            children[i] = null;
        }
    }
}

class Trie {
    public TrieNode root;

    public Trie() {
        root = new TrieNode();
    }

    // insert a word into the trie
    public void Insert(string word) {
        TrieNode node = root;
        foreach (char ch in word) {
            int idx = ch - 'a';
            if (node.children[idx] == null) {
                node.children[idx] = new TrieNode();
            }
            node = node.children[idx];
        }
        // marking the end of the word
        node.isEnd = true;
    }

    // check if all prefixes of the word exist in the trie
    public bool AllPrefixesExist(string word) {
        TrieNode node = root;
        foreach (char ch in word) {
            int idx = ch - 'a';
            node = node.children[idx];

            // if the prefix is missing or not marked as end
            if (node == null || !node.isEnd) {
                return false;
            }
        }
        return true;
    }
}

class GfG {

    // Function to find the longest word whose 
    // all prefixes exist in the list
    public static string longestString(string[] arr) {
        Trie trie = new Trie();

        // insert all words into the trie
        foreach (string word in arr) {
            trie.Insert(word);
        }

        string result = "";

        // check each word
        foreach (string word in arr) {

            // if all prefixes exist
            if (trie.AllPrefixesExist(word)) {

                // update result if word is longer or 
                // lexicographically smaller
                if (word.Length > result.Length ||
                    (word.Length == result.Length &&
                            string.Compare(word, result) < 0)) {
                    result = word;
                }
            }
        }

        return result;
    }

    public static void Main(string[] args) {
        string[] arr = { "ab", "a", "abc", "abd" };
        Console.WriteLine(longestString(arr));
    }
}
JavaScript
// trie node definition
class TrieNode {
    constructor() {
        this.children = new Array(26).fill(null);
        this.isEnd = false;
    }
}

class Trie {
    constructor() {
        this.root = new TrieNode();
    }

    // insert a word into the trie
    insert(word) {
        let node = this.root;
        for (let ch of word) {
            let idx = ch.charCodeAt(0) - 97;
            if (!node.children[idx]) {
                node.children[idx] = new TrieNode();
            }
            node = node.children[idx];
        }
        // marking the end of the word
        node.isEnd = true;
    }

    // check if all prefixes of the word exist in the trie
    allPrefixesExist(word) {
        let node = this.root;
        for (let ch of word) {
            let idx = ch.charCodeAt(0) - 97;
            node = node.children[idx];

            // if the prefix is missing or not marked as end
            if (!node || !node.isEnd) {
                return false;
            }
        }
        return true;
    }
}

// Function to find the longest word whose 
// all prefixes exist in the list
function longestString(arr) {
    let trie = new Trie();

    // insert all words into the trie
    for (let word of arr) {
        trie.insert(word);
    }

    let result = "";
    
    // check each word
    for (let word of arr) {

        // if all prefixes exist
        if (trie.allPrefixesExist(word)) {

            // update result if word is longer or 
            // lexicographically smaller
            if (word.length > result.length ||
                (word.length === result.length && word < result)) {
                result = word;
            }
        }
    }

    return result;
}

// Driver Code
const arr = ["ab", "a", "abc", "abd"];
console.log(longestString(arr));

Output
abc

Time Complexity: O(n*k), one pass for insertion and one for prefix checks where n is the number of words and k is the average word length,
Auxiliary Space: O(n*k), where n is the number of words and k is the average word length.


Article Tags :

Explore