我一直在努力解决这个问题SPOJ www.spoj.com/problems/PRHYME/?好几天了,但都没有成功。 以下是问题所在:
给出的是单词列表L和单词w。你的任务是在L中找到一个与w形成完美押韵的单词。这个词u由这些属性唯一确定:
- 在L。
- 与w。
不同- 他们的共同后缀是尽可能长的。
- 在满足前面几点的所有单词中,u是按字典顺序排列的最小单词。
单词的长度将是< = 30 字典和查询中的单词数量可以是2,50,000。
我正在使用trie来存储字典中的所有单词。 然后,为了解决查询,我按以下方式进行: -
当我在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;
}
答案 0 :(得分:4)
你的算法(使用存储所有反转词的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
,则可能更容易实现。