Assign Short Codes in Stream of Words

Last Updated : 20 Jul, 2026

A railway department is renaming several cities, and the corresponding railway station codes must also be updated.

Given an array cities[] containing the names of n cities in the order they are renamed. Generate the station code for each city according to the following rules:

  • If a city appears for the first time, its station code should be the shortest prefix of its name that is not a prefix of any previously processed city.
  • If the city has already appeared before, then no unique prefix can be assigned. Instead, its station code should be the complete city name, followed by a space and its occurrence count (including the current occurrence).

Return an array containing the generated station codes for all the cities in the given order.

Examples:

Input: cities[] = ["berlin", "bremen", "munich", "bonn", "berlin", "bochum"]
Output: ["b", "br", "m", "bo", "berlin 2", "boc"]
Explanation:
-> berlin is the first city, so b is assigned.
-> bremen cannot use b, so the shortest unique prefix is br.
-> munich starts with a new prefix, so m is assigned.
-> bonn shares b with previous cities, so bo is assigned.
-> bochum shares b and bo, so the shortest unique prefix is boc.

Input: cities[] = ["rimini", "milan", "rome", "naples", "ravenna", "rome"]
Output: ["r", "m", "ro", "n", "ra", "rome 2"]
Explanation:
-> rimini is the first city, so r is assigned.
-> milan starts with a new prefix, so m is assigned.
-> rome cannot use r, so the shortest unique prefix is ro.
-> naples starts with a new prefix, so n is assigned.
-> ravenna shares r with previous cities, so the shortest unique prefix is ra.
-> rome appears for the second time, so its station code becomes rome 2.

Try It Yourself
redirect icon

[Naive Approach] Check Every Prefix Against Previously Processed Cities

The idea is to process the cities one by one. For each city appearing for the first time, try all its prefixes from shortest to longest and check whether the prefix is a prefix of any previously processed city. The first valid prefix is assigned as the station code. If the city has already appeared before, return the complete city name followed by its occurrence count.

Working of Approach:

  • Process the cities one by one while maintaining the occurrence count of each city.
  • If a city appears again, append its occurrence count to the complete city name.
  • For a new city, try all prefixes from shortest to longest.
  • For each prefix, compare it with all previously processed cities and check whether it is a prefix of any of them.
  • The first prefix that is not a prefix of any previous city is assigned as the station code.
C++
#include <climits>
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
using namespace std;

// Function to assign station codes
vector<string> renameCities(vector<string> &cities)
{
    unordered_map<string, int> freq;
    vector<string> res;

    // Process each city
    for (int i = 0; i < cities.size(); i++)
    {
        freq[cities[i]]++;

        // If city is repeated, append occurrence count
        if (freq[cities[i]] > 1)
        {
            res.push_back(cities[i] + " " + to_string(freq[cities[i]]));
            continue;
        }

        string ans = cities[i];

        // Try every prefix of the current city
        for (int len = 1; len <= cities[i].size(); len++)
        {
            string pref = cities[i].substr(0, len);
            bool ok = true;

            // Check whether the prefix is used by any previous city
            for (int j = 0; j < i; j++)
            {
                if (cities[j].substr(0, min((int)cities[j].size(), len)) == pref)
                {
                    ok = false;
                    break;
                }
            }

            // Found the shortest unique prefix
            if (ok)
            {
                ans = pref;
                break;
            }
        }

        res.push_back(ans);
    }

    return res;
}

int main()
{
    vector<string> cities = {"rimini", "milan", "rome", "naples", "ravenna", "rome"};

    vector<string> res = renameCities(cities);

    cout << "[";

    for (int i = 0; i < res.size(); i++)
    {
        cout << "\"" << res[i] << "\"";

        if (i + 1 != res.size())
            cout << ", ";
    }

    cout << "]";

    return 0;
}
Java
import java.util.ArrayList;
import java.util.HashMap;

class GFG {

    // Function to assign station codes
    public ArrayList<String> renameCities(String[] cities)
    {

        HashMap<String, Integer> freq = new HashMap<>();
        ArrayList<String> res = new ArrayList<>();

        // Process each city
        for (int i = 0; i < cities.length; i++) {

            freq.put(cities[i],
                     freq.getOrDefault(cities[i], 0) + 1);

            // If city is repeated, append occurrence count
            if (freq.get(cities[i]) > 1) {
                res.add(cities[i] + " "
                        + freq.get(cities[i]));
                continue;
            }

            String ans = cities[i];

            // Try every prefix of the current city
            for (int len = 1; len <= cities[i].length();
                 len++) {

                String pref = cities[i].substring(0, len);
                boolean ok = true;

                // Check whether the prefix is used by any
                // previous city
                for (int j = 0; j < i; j++) {

                    if (cities[j]
                            .substring(
                                0,
                                Math.min(cities[j].length(),
                                         len))
                            .equals(pref)) {
                        ok = false;
                        break;
                    }
                }

                // Found the shortest unique prefix
                if (ok) {
                    ans = pref;
                    break;
                }
            }

            res.add(ans);
        }

        return res;
    }

    // Driver code
    public static void main(String[] args)
    {

        String[] cities = { "rimini", "milan",   "rome",
                            "naples", "ravenna", "rome" };

        GFG obj = new GFG();
        ArrayList<String> res = obj.renameCities(cities);

        System.out.print("[");

        for (int i = 0; i < res.size(); i++) {
            System.out.print("\"" + res.get(i) + "\"");

            if (i + 1 != res.size())
                System.out.print(", ");
        }

        System.out.print("]");
    }
}
Python
# Function to assign station codes
def renameCities(cities):
    freq = {}
    res = []

    # Process each city
    for i in range(len(cities)):

        freq[cities[i]] = freq.get(cities[i], 0) + 1

        # If city is repeated, append occurrence count
        if freq[cities[i]] > 1:
            res.append(cities[i] + " " + str(freq[cities[i]]))
            continue

        ans = cities[i]

        # Try every prefix of the current city
        for length in range(1, len(cities[i]) + 1):

            pref = cities[i][:length]
            ok = True

            # Check whether the prefix is used by any previous city
            for j in range(i):

                if cities[j][:min(len(cities[j]), length)] == pref:
                    ok = False
                    break

            # Found the shortest unique prefix
            if ok:
                ans = pref
                break

        res.append(ans)

    return res


if __name__ == "__main__":

    cities = ["rimini", "milan", "rome", "naples", "ravenna", "rome"]

    res = renameCities(cities)

    print("[", end="")

    for i in range(len(res)):
        print(f"\"{res[i]}\"", end="")

        if i + 1 != len(res):
            print(", ", end="")

    print("]")
C#
using System;
using System.Collections.Generic;

class GFG {
    // Function to assign station codes
    public List<string> renameCities(string[] cities)
    {
        Dictionary<string, int> freq
            = new Dictionary<string, int>();
        List<string> res = new List<string>();

        // Process each city
        for (int i = 0; i < cities.Length; i++) {
            if (!freq.ContainsKey(cities[i]))
                freq[cities[i]] = 0;

            freq[cities[i]]++;

            // If city is repeated, append occurrence count
            if (freq[cities[i]] > 1) {
                res.Add(cities[i] + " " + freq[cities[i]]);
                continue;
            }

            string ans = cities[i];

            // Try every prefix of the current city
            for (int len = 1; len <= cities[i].Length;
                 len++) {
                string pref = cities[i].Substring(0, len);
                bool ok = true;

                // Check whether the prefix is used by any
                // previous city
                for (int j = 0; j < i; j++) {
                    if (cities[j].Substring(
                            0,
                            Math.Min(cities[j].Length, len))
                        == pref) {
                        ok = false;
                        break;
                    }
                }

                // Found the shortest unique prefix
                if (ok) {
                    ans = pref;
                    break;
                }
            }

            res.Add(ans);
        }

        return res;
    }

    // Driver code
    static void Main()
    {
        string[] cities = { "rimini", "milan",   "rome",
                            "naples", "ravenna", "rome" };

        GFG obj = new GFG();
        List<string> res = obj.renameCities(cities);

        Console.Write("[");

        for (int i = 0; i < res.Count; i++) {
            Console.Write("\"" + res[i] + "\"");

            if (i + 1 != res.Count)
                Console.Write(", ");
        }

        Console.Write("]");
    }
}
JavaScript
function renameCities(cities)
{
    let freq = new Map();
    let res = [];

    // Process each city
    for (let i = 0; i < cities.length; i++) {
        if (!freq.has(cities[i]))
            freq.set(cities[i], 0);
        freq.set(cities[i], freq.get(cities[i]) + 1);

        // If city is repeated, append occurrence count
        if (freq.get(cities[i]) > 1) {
            res.push(cities[i] + " " + freq.get(cities[i]));
            continue;
        }

        let ans = cities[i];

        // Try every prefix of the current city
        for (let len = 1; len <= cities[i].length; len++) {
            let pref = cities[i].substring(0, len);
            let ok = true;

            // Check whether the prefix is used by any
            // previous city
            for (let j = 0; j < i; j++) {
                if (cities[j].substring(
                        0, Math.min(cities[j].length, len))
                    === pref) {
                    ok = false;
                    break;
                }
            }

            // Found the shortest unique prefix
            if (ok) {
                ans = pref;
                break;
            }
        }

        res.push(ans);
    }

    return res;
}

// Driver Code
let cities = ["rimini", "milan", "rome", "naples", "ravenna", "rome"];
let res = renameCities(cities);
console.log("[");
for (let i = 0; i < res.length; i++) {
    process.stdout.write("\"" + res[i] + "\"");

    if (i + 1 != res.length)
        process.stdout.write(", ");
}
console.log("]");

Output
["r", "m", "ro", "n", "ra", "rome 2"]

Time Complexity: O(n ^ 2 * L), where n is the number of cities and L is the maximum length of a city name, as each new city may compare its prefixes with all previously processed cities.
Space Complexity: O(n), as a frequency map is used to store the occurrence count of each city.

[Expected Approach] Using Trie

The idea is to store all previously processed cities in a Trie. While searching for a city, the first character whose Trie node does not exist gives the shortest unique prefix. If the complete city already exists in the Trie, it is a repeated city, so append its occurrence count. Otherwise, insert the city into the Trie.

Working of Approach:

  • Store all previously processed city names in a Trie and maintain the occurrence count of each city.
  • For every city, traverse the Trie character by character to find the first character whose Trie node does not exist.
  • The prefix ending at that character is the shortest unique prefix for the city.
  • If the complete city already exists in the Trie, append its occurrence count to the city name instead of assigning a prefix.
  • Otherwise, insert the city into the Trie and add the shortest unique prefix to the answer.

Let us understand with an example:
Input: cities[] = ["rimini", "milan", "rome", "naples", "ravenna", "rome"]

  • Initially, the Trie is empty and the occurrence count of every city is 0.
  • Process each city one by one. For a city appearing for the first time, traverse the Trie character by character and stop at the first character whose Trie node does not exist. The prefix ending at that character becomes its station code.
  • If the city is appearing for the first time, insert its complete name into the Trie and record its occurrence count as 1.
  • For the given input, the station codes assigned to the first five cities are "r", "m", "ro", "n", and "ra" respectively.
  • When "rome" appears again, the complete city is already present in the Trie. Increment its occurrence count and append it to the city name, producing "rome 2".
  • Hence, the final output is ["r", "m", "ro", "n", "ra", "rome 2"].
C++
#include <climits>
#include <iostream>
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
using namespace std;

class Node
{
  public:
    bool isEndOfWord;
    map<char, Node *> mp;

    Node()
    {
        isEndOfWord = false;
    }
};

// Function to insert a city into the Trie.
void insertInTrie(Node *root, string &s)
{
    for (char ch : s)
    {
        if (root->mp[ch] == nullptr)
            root->mp[ch] = new Node();

        root = root->mp[ch];
    }

    root->isEndOfWord = true;
}

// Returns the last index of the shortest unique prefix.
// Returns INT_MAX if the city already exists in the Trie.
int searchInTrie(Node *root, string &s)
{

    for (int i = 0; i < (int)s.length(); i++)
    {

        if (root->mp[s[i]] == nullptr)
            return i;

        root = root->mp[s[i]];
    }

    // City already exists.
    if (root->isEndOfWord)
        return INT_MAX;

    return s.length() - 1;
}

vector<string> renameCities(vector<string> &cities)
{
    vector<string> res;
    map<string, int> freqMap;

    Node *root = new Node();

    for (string &city : cities)
    {

        int idx = searchInTrie(root, city);

        // City already processed before.
        if (idx == INT_MAX)
        {
            freqMap[city]++;
            res.push_back(city + " " + to_string(freqMap[city]));
        }
        // First occurrence.
        else
        {
            insertInTrie(root, city);
            freqMap[city] = 1;
            res.push_back(city.substr(0, idx + 1));
        }
    }

    return res;
}

int main()
{
    vector<string> cities = {"rimini", "milan", "rome", "naples", "ravenna", "rome"};

    vector<string> res = renameCities(cities);

    cout << "[";

    for (int i = 0; i < res.size(); i++)
    {
        cout << "\"" << res[i] << "\"";

        if (i + 1 != res.size())
            cout << ", ";
    }

    cout << "]";

    return 0;
}
Java
import java.util.ArrayList;
import java.util.HashMap;

class Node {
    boolean isEndOfWord;
    HashMap<Character, Node> mp;

    Node()
    {
        isEndOfWord = false;
        mp = new HashMap<>();
    }
}

class GFG {

    // Function to insert a city into the Trie.
    void insertInTrie(Node root, StringBuilder s)
    {
        for (char ch : s.toString().toCharArray()) {
            if (root.mp.get(ch) == null)
                root.mp.put(ch, new Node());

            root = root.mp.get(ch);
        }

        root.isEndOfWord = true;
    }

    // Returns the last index of the shortest unique prefix.
    // Returns Integer.MAX_VALUE if the city already exists
    // in the Trie.
    int searchInTrie(Node root, StringBuilder s)
    {

        for (int i = 0; i < s.length(); i++) {

            if (root.mp.get(s.charAt(i)) == null)
                return i;

            root = root.mp.get(s.charAt(i));
        }

        // City already exists.
        if (root.isEndOfWord)
            return Integer.MAX_VALUE;

        return s.length() - 1;
    }

    public ArrayList<String> renameCities(String[] cities)
    {

        ArrayList<String> res = new ArrayList<>();
        HashMap<String, Integer> freqMap = new HashMap<>();

        Node root = new Node();

        for (String city : cities) {

            StringBuilder cityBuilder
                = new StringBuilder(city);
            int idx = searchInTrie(root, cityBuilder);

            // City already processed before.
            if (idx == Integer.MAX_VALUE) {
                freqMap.put(city,
                            freqMap.getOrDefault(city, 0)
                                + 1);
                res.add(city + " " + freqMap.get(city));
            }
            // First occurrence.
            else {
                insertInTrie(root, cityBuilder);
                freqMap.put(city, 1);
                res.add(city.substring(0, idx + 1));
            }
        }

        return res;
    }

    // Driver code
    public static void main(String[] args)
    {

        String[] cities = { "rimini", "milan",   "rome",
                            "naples", "ravenna", "rome" };

        GFG obj = new GFG();
        ArrayList<String> res = obj.renameCities(cities);

        System.out.print("[");

        for (int i = 0; i < res.size(); i++) {
            System.out.print("\"" + res.get(i) + "\"");

            if (i + 1 != res.size())
                System.out.print(", ");
        }

        System.out.print("]");
    }
}
Python
class Node:
    def __init__(self):
        self.isEndOfWord = False
        self.mp = {}


# Function to insert a city into the Trie.
def insertInTrie(root, s):
    for ch in s:
        if ch not in root.mp:
            root.mp[ch] = Node()

        root = root.mp[ch]

    root.isEndOfWord = True


# Returns the last index of the shortest unique prefix.
# Returns float('inf') if the city already exists in the Trie.
def searchInTrie(root, s):

    for i in range(len(s)):

        if s[i] not in root.mp:
            return i

        root = root.mp[s[i]]

    # City already exists.
    if root.isEndOfWord:
        return float("inf")

    return len(s) - 1


def renameCities(cities):

    res = []
    freqMap = {}

    root = Node()

    for city in cities:

        idx = searchInTrie(root, city)

        # City already processed before.
        if idx == float("inf"):
            freqMap[city] = freqMap.get(city, 0) + 1
            res.append(city + " " + str(freqMap[city]))
        # First occurrence.
        else:
            insertInTrie(root, city)
            freqMap[city] = 1
            res.append(city[:idx + 1])

    return res


if __name__ == "__main__":

    cities = ["rimini", "milan", "rome", "naples", "ravenna", "rome"]

    res = renameCities(cities)

    print("[", end="")

    for i in range(len(res)):
        print(f"\"{res[i]}\"", end="")

        if i + 1 != len(res):
            print(", ", end="")

    print("]")
C#
using System;
using System.Collections.Generic;

class Node {
    public bool isEndOfWord;
    public Dictionary<char, Node> mp;

    public Node()
    {
        isEndOfWord = false;
        mp = new Dictionary<char, Node>();
    }
}

class GFG {
    // Function to insert a city into the Trie.
    void insertInTrie(Node root, string s)
    {
        foreach(char ch in s)
        {
            if (!root.mp.ContainsKey(ch))
                root.mp[ch] = new Node();

            root = root.mp[ch];
        }

        root.isEndOfWord = true;
    }

    // Returns the last index of the shortest unique prefix.
    // Returns int.MaxValue if the city already exists in
    // the Trie.
    int searchInTrie(Node root, string s)
    {
        for (int i = 0; i < s.Length; i++) {
            if (!root.mp.ContainsKey(s[i]))
                return i;

            root = root.mp[s[i]];
        }

        // City already exists.
        if (root.isEndOfWord)
            return int.MaxValue;

        return s.Length - 1;
    }

    public List<string> renameCities(string[] cities)
    {
        List<string> res = new List<string>();
        Dictionary<string, int> freqMap
            = new Dictionary<string, int>();

        Node root = new Node();

        foreach(string city in cities)
        {
            int idx = searchInTrie(root, city);

            // City already processed before.
            if (idx == int.MaxValue) {
                freqMap[city]++;
                res.Add(city + " " + freqMap[city]);
            }
            // First occurrence.
            else {
                insertInTrie(root, city);
                freqMap[city] = 1;
                res.Add(city.Substring(0, idx + 1));
            }
        }

        return res;
    }

    // Driver code
    static void Main()
    {
        string[] cities = { "rimini", "milan",   "rome",
                            "naples", "ravenna", "rome" };

        GFG obj = new GFG();
        List<string> res = obj.renameCities(cities);

        Console.Write("[");

        for (int i = 0; i < res.Count; i++) {
            Console.Write("\"" + res[i] + "\"");

            if (i + 1 != res.Count)
                Console.Write(", ");
        }

        Console.Write("]");
    }
}
JavaScript
class Node {
    constructor()
    {
        this.isEndOfWord = false;
        this.mp = new Map();
    }
}

// Function to insert a city into the Trie.
function insertInTrie(root, s)
{
    for (let ch of s) {
        if (!root.mp.has(ch))
            root.mp.set(ch, new Node());

        root = root.mp.get(ch);
    }

    root.isEndOfWord = true;
}

// Returns the last index of the shortest unique prefix.
// Returns Number.MAX_SAFE_INTEGER if the city already
// exists in the Trie.
function searchInTrie(root, s)
{

    for (let i = 0; i < s.length; i++) {

        if (!root.mp.has(s[i]))
            return i;

        root = root.mp.get(s[i]);
    }

    // City already exists.
    if (root.isEndOfWord)
        return Number.MAX_SAFE_INTEGER;

    return s.length - 1;
}

function renameCities(cities)
{

    let res = [];
    let freqMap = new Map();

    let root = new Node();

    for (let city of cities) {

        let idx = searchInTrie(root, city);

        // City already processed before.
        if (idx === Number.MAX_SAFE_INTEGER) {
            freqMap.set(city, (freqMap.get(city) || 0) + 1);
            res.push(city + " " + freqMap.get(city));
        }
        // First occurrence.
        else {
            insertInTrie(root, city);
            freqMap.set(city, 1);
            res.push(city.substring(0, idx + 1));
        }
    }

    return res;
}

// Driver Code
let cities = ["rimini", "milan", "rome", "naples", "ravenna", "rome"];
let res = renameCities(cities);
console.log("[");
for (let i = 0; i < res.length; i++) {
    process.stdout.write("\"" + res[i] + "\"");

    if (i + 1 != res.length)
        process.stdout.write(", ");
}
console.log("]");

Output
["r", "m", "ro", "n", "ra", "rome 2"]

Time Complexity: O(n × L × log 26), where n is the number of cities and L is the maximum length of a city name. Each Trie operation on a map takes O(log 26) time.
Space Complexity: O(n × L), where n is the number of cities and L is the maximum length of a city name, as the Trie stores at most all characters of the distinct city names.

Comment