简体   繁体   中英

Trie, prefix for phonenumbers

I a building a program that takes phone numbers as input. It should then check if there already exists a phone number which is a prefix in our new number. Example:

Input:
555 //This is okay
5556888 //This is not okay because 555 is a registered number
556888 //this is okay
5568889 // Not okay

Hope you understand what I'm getting at.

I have implemented two functions:

Contains That should check if the number or prefixes already exist.

bool PrefixStringSet::contains(string s)
{
    NodePtr temp = root;
    for ( int i = 0; i < s.size(); i++)
    {
        if(temp->children[s[i] - '0'] == NULL)
        {
            return false;
        }
        temp = temp->children[s[i] - '0'];
    }
    return true;
}

Insert

bool PrefixStringSet::insert(string s)
{
    if(contains(s) == true)
    {
        return false;
    }
    NodePtr temp = root;
    for (int i = 0; i < s.size(); i++)
    {
        int number = (int)s[i] - (int)'0';
        if(temp->children[number] == NULL)
        {
            temp->children[number] = new TrieNode;
            temp = temp->children[number];
        }
    }
    return true;
}

As it stands now contains only check's if the number has already been registered. I can't figure out a good way to check if the prefix is already a number. Should I implement it in contains or maybe in the insert function(Maybe have a loop go trough each prefix, starting with the first number?)

Any help appreciated.

Main

int main()
{
    PrefixStringSet Phonenumber;
   int HowManyPhoneNumbers;
   cin >> HowManyPhoneNumbers;
   for(int i = 0 ; i<HowManyPhoneNumbers ; i++)
   {

       string temp;
       cin >> temp;
       if(Phonenumber.insert(temp) == true)
       {
           cout << "Yes" << endl;
       }
       else
       {
           cout << "NO" <<endl;
       }

   }
  return 0;
}

Edit Insert:

    bool PrefixStringSet::insert(string s)
    {
       if(contains(s) == true)
        {
            return false;
        }
        NodePtr temp = root;
        for (int i = 0; i < s.size(); i++)
        {
            int number = (int)s[i] - (int)'0';
            if(temp->children[number] == NULL)
            {
                temp->children[number] = new TrieNode;
            }
            temp = temp->children[number];
        }
        return true;
    }

Contains:
bool PrefixStringSet::contains(string s)
{
    NodePtr temp = root;
    for ( int i = 0; i < s.size(); i++)
    {
        if(temp->is_leaf())
        {
            return false;
        }
        if(temp->children[s[i] - '0'] == NULL)
        {
            return false;
        }
        temp = temp->children[s[i] - '0'];
    }
    return true;
}

input:
 911
YES
9111 /Not working
Yes
91 //Working
NO

edit2:

bool PrefixStringSet::contains(string s)
{
    NodePtr temp = root;
    for ( int i = 0; i < s.size(); i++)
    {
        if(temp->is_leaf())
        {
            return false;
        }
        if (temp !=root &&  temp->is_leaf())
        {
            return true;
        }
        if(temp->children[s[i] - '0'] == NULL)
        {
            return false;
        }
        temp = temp->children[s[i] - '0'];
    }
    return true;
}

In PrefixStringSet::contains(string s)

    if(temp->children[s[i] - '0'] == NULL)
    {
        return false;
    }

Instead of returning false directly, you should check whether temp has any non-NULL child. That is,

    if(temp->children[s[i] - '0'] == NULL)
    {
        return (temp != root && temp->is_leaf());
    }

Because if temp has no child at all, then the string s 's prefix already exists.
Edit: Check temp != root to avoid being stuck at empty trie.

The most efficient implementation of TrieNode::hasAnyChild() depends on how TrieNode::children was stored, which you didn't show in the question statement. If your trie only accepts decimal digits, then simply go through all children should be good enough.

By the way, in PrefixStringSet::insert(string s)

    int number = (int)s[i] - (int)'0';
    if(temp->children[number] == NULL)
    {
        temp->children[number] = new TrieNode;
        temp = temp->children[number];
    }

The line temp = temp->children[number]; should be moved after the end of if block since you need to move temp one step further no matter you created a new node or not.

In a standard Trie, you should have another field in your Node struct to indicate whether it indicates a word.

bool PrefixStringSet::contains(string s)
{
    NodePtr temp = root;
    for ( int i = 0; i < s.size(); i++)
    {
        if(temp->isWord)
        {
            return false;
        }
        if(temp->children[s[i] - '0'] == NULL)
        {
            return false;
        }
        temp = temp->children[s[i] - '0'];
    }
    return true;
}

bool PrefixStringSet::insert(string s)
{
    if(contains(s) == true)
    {
        return false;
    }
    NodePtr temp = root;
    for (int i = 0; i < s.size(); i++)
    {
        int number = (int)s[i] - (int)'0';
        if(temp->children[number] == NULL)
        {
            temp->children[number] = new TrieNode;
        }
        temp = temp->children[number];
    }
    temp->isWord = true;
    return true;
}

However, in your problem, only a LEAF node indicates a existing word, because you don't allow any number to be a prefix of another number in this Trie.

So you can check whether a Node is a leaf node by iterating its children as @timaru says. But it's not very efficient.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM