解决 SPOJ www.spoj.com/problems/PRHYME/ 的正确方法是什么?

What's the correct approach to solve SPOJ www.spoj.com/problems/PRHYME/?

本文关键字:方法 是什么 problems www SPOJ spoj com 解决 PRHYME      更新时间:2023-10-16

我一直在努力解决这个问题SPOJ www.SPOJ.com/problems/PRHYME/?好几天了,但都没有成功。问题简单来说如下:

Given是一个单词列表L和一个单词w。你的任务是在L中找到一个与w形成完美押韵的单词。这个单词u由以下属性唯一确定:

  • 它在L
  • 它与w不同
  • 它们的通用后缀尽可能长
  • 在所有满足前面几点的单词中,u是字典中最小的一个

单词的长度将是<30.
字典和查询中的单词数量都可以是2,50000。

我正在用trie把字典里的所有单词都倒过来存储。然后,为了解决查询,我按照以下方式进行:-

  1. 如果单词出现在trie中,请将其从trie中删除
  2. 现在从根遍历trie,直到查询字符串中的字符与trie值匹配为止。让找到最后一个字符匹配的点是P
  3. 现在,从P点开始,我使用DFS遍历trie,在遇到叶节点时,将形成的字符串推送到可能的结果列表中
  4. 现在,我从这个列表中返回按字典顺序最小的结果

当我在SPOJ上提交解决方案时,我的解决方案会出现超时错误。

有人能提出一个详细的算法或提示来解决这个问题吗?如果需要,我可以发布我的代码。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<vector>
#include<string>
#include<algorithm>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<map>
#include<queue>
#include<set>
#define  ll long long signed int
#define ull unsigned long long int 

const int alpha=26;

using namespace std;
struct node
{
    int value;
    node * child[alpha]; 
};
node * newnode()
{
    node * newt=new node;
    newt->value=0;
    for(int i=0;i<alpha;i++)
    {
        newt->child[i]=NULL;
    }
    return newt;
}
struct trie
{
   node * root;
   int count;
   trie()
   {
      count=0;
      root=newnode();
   }
};
trie * dict=new trie;

string reverse(string s)
{
   int l=s.length();
   string rev=s;
   for(int i=0;i<l;i++)
   {
       int j=l-1-i;
       rev[j]=s[i];
   }
   return rev;  
}
void insert(string s)
{
    int l=s.length();
    node * ptr=dict->root;
    dict->count++;
    for(int i=0;i<l;i++)
    {
       int index=s[i]-'a';
       if(ptr->child[index]==NULL)
       {
         ptr->child[index]=newnode();
       }
       ptr=ptr->child[index];
    }
    ptr->value=dict->count;
}
void dfs1(node *ptr,string p)
{
   if(ptr==NULL) return;
   if(ptr->value)  cout<<"word" <<p<<endl;
   for(int i=0;i<26;i++)
   {    
         if(ptr->child[i]!=NULL)
         dfs1(ptr->child[i],p+char('a'+i));
   }
}
vector<string> results;
pair<node *,string> search(string s)
{
    int l=s.length();
    node * ptr=dict->root;
    node *save=ptr;
    string match="";
    int i=0;
    bool no_match=false;
    while(i<l and !no_match)
    {
        int in=s[i]-'a';
        if(ptr->child[in]==NULL)
        {
          save=ptr;
          no_match=true;
        }
        else
        {
           ptr=ptr->child[in];
           save=ptr;
           match+=in+'a';
        }
        i++;
    }
    //cout<<s<<" matched till here"<<match <<" "<<endl;
    return make_pair(save,match);

}
bool  find(string s)
{
    int l=s.length();
    node * ptr=dict->root;
    string match="";
    for(int i=0;i<l;i++)
    {
          int in=s[i]-'a';
          //cout<<match<<"match"<<endl;
         if(ptr->child[in]==NULL)
         {
           return false;
         }
         ptr=ptr->child[in];
        match+=char(in+'a');
    }
    //cout<<match<<"match"<<endl;
    return true;

}
bool leafNode(node *pNode)
{
    return (pNode->value != 0);
}
bool isItFreeNode(node *pNode)
{
    int i;
    for(i = 0; i < alpha; i++)
    {
        if( pNode->child[i] )
            return false;
    }
    return true;
}
bool deleteHelper(node *pNode, string key, int level, int len)
{
    if( pNode )
    {
        // Base case
        if( level == len )
        {
            if( pNode->value )
            {
                // Unmark leaf node
                pNode->value = 0;
                // If empty, node to be deleted
                if( isItFreeNode(pNode) )
                {
                    return true;
                }
                return false;
            }
        }
        else // Recursive case
        {
            int index = (key[level])-'a';
            if( deleteHelper(pNode->child[index], key, level+1, len) )
            {
                // last node marked, delete it
                free(pNode->child[index]);
                pNode->child[index]=NULL;
                // recursively climb up, and delete eligible nodes
                return ( !leafNode(pNode) && isItFreeNode(pNode) );
            }
        }
    }
    return false;
}
void deleteKey(string key)
{
    int len = key.length();
    if( len > 0 )
    {
        deleteHelper(dict->root, key, 0, len);
    }
}
string result="***";
void dfs(node *ptr,string p)
{
   if(ptr==NULL) return;
   if(ptr->value )  
   {
       if((result)=="***")
       {
          result=reverse(p);
       }
       else
       {
          result=min(result,reverse(p));
       }
   }
   for(int i=0;i<26;i++)
   {    
         if(ptr->child[i]!=NULL)
         dfs(ptr->child[i],p+char('a'+i));
   }
}
int main(int argc ,char ** argv)
{
         #ifndef ONLINE_JUDGE
         freopen("prhyme.in","r",stdin);
         #endif
        string s;
         while(getline(cin,s,'n'))
         {

            if(s[0]<'a' and s[0]>'z')
            break;
           int l=s.length();
           if(l==0) break;
           string  rev;//=new char[l+1];
           rev=reverse(s);
        insert(rev);
        //cout<<"...........traverse..........."<<endl;
        //dfs(dict->root);       
        //cout<<"..............traverse end.............."<<endl;         
         }      
          while(getline(cin,s))
         {

            results.clear();
            //cout<<s<<endl;
            int l=s.length();
            if(!l) break;
            string rev;//=new char[l+1];
            rev=reverse(s);
            //cout<<rev<<endl;
            bool del=false;
            if(find(rev))
            {
              del=true;
              //cout<<"here found"<<endl;
              deleteKey(rev);
            }
             if(find(rev))
            {
              del=true;
              //cout<<"here found"<<endl;
              deleteKey(rev);
            }
            else
            {
              //cout<<"not here found"<<endl;
            }
          // cout<<"...........traverse..........."<<endl;
        //dfs1(dict->root,"");       
     // cout<<"..............traverse end.............."<<endl;         
            pair<node *,string> pp=search(rev);
            result="***";   
            dfs(pp.first,pp.second);
            //cout<<"search results"<<endl;
            //dfs1(pp.first,pp.second);
            //cout<<"end of search results"<<
            for(int i=0;i<results.size();i++)
            {
               results[i]=reverse(results[i]);
              // cout<<s<<" "<<results[i]<<endl;
            }
            string smin=result;
            if(del)
            {
               insert(rev);
            }
            cout<<smin<<endl;
         }  
    return 0;
}

您的算法(使用存储所有反转单词的trie)是一个良好的开端。但它的一个问题是,对于每次查找,你必须枚举所有带有特定后缀的单词,才能找到字典中最小的一个。在某些情况下,这可能是一项艰巨的工作。

解决这个问题的一种方法是:在每个节点(对应于每个后缀)中,存储具有该后缀的两个字典中最小的单词。在构建trie时,通过更新每个新添加的叶的所有祖先节点,这很容易维护(请参阅下面的伪代码)。

然后,要执行单词w的查找,请从该单词对应的节点开始,然后在树中向上移动,直到到达包含除w以外的子代单词的节点。然后返回存储在该节点中的字典上最小的单词,或者在最小值等于w的情况下返回第二小的单词。

要创建trie,可以使用以下伪代码:

for each word:
    add word to trie
    let n be the node corresponding to the new word.
    for each ancestor a of n (including n):
        if a.smallest==null or word < a.smallest:
            a.second_smallest = a.smallest
            a.smallest = word
        else if a.second_smallest==null or word < a.second_smallest:
            a.second_smallest = word

查找单词w:

let n be the node corresponding to longest possible suffix of w.
while ((n.smallest==w || n.smallest==null) && 
       (n.second_smallest==w || n.second_smallest==null)):
    n = n.parent
if n.smallest==w:
    return n.second_smallest
else:
    return n.smallest

另一种类似的可能性是使用哈希表将所有后缀映射到两个字典上最小的单词,而不是使用trie。如果可以使用std::unordered_map,这可能更容易实现。