前面有一个面试问题,并且在基本语法上窒息得太厉害,我没能推进(一旦肾上腺素开始,编码就会消失。)
给定一个字符串列表,返回一组字符串,这些字符串是输入集的字谜。即“狗”,“上帝”,“foo”应该返回{“dog”,“god”}。之后,我自己创建了代码作为一个完整性检查,它现在已经存在了一段时间。我欢迎对它进行输入,看看我是否遗漏了任何东西,或者我是否可以更有效地完成任务。把它当作改善自己和学习其他技巧的机会:
void Anagram::doWork(list input, list> &output)
{
typedef list < pair < string, string>> SortType;
SortType sortedInput;
// sort each string and pair it with the original
for(list< string >::iterator i = input.begin(); i != input.end(); ++i)
{
string tempString(*i);
std::sort(tempString.begin(), tempString.end());
sortedInput.push_back(make_pair(*i, tempString));
}
// Now step through the new sorted list
for(SortType::iterator i = sortedInput.begin(); i != sortedInput.end();)
{
set< string > newSet;
// Assume (hope) we have a match and pre-add the first.
newSet.insert(i->first);
// Set the secondary iterator one past the outside to prevent
// matching the original
SortType::iterator j = i;
++j;
while(j != sortedInput.end())
{
if(i->second == j->second)
{
// If the string matches, add it to the set and remove it
// so that future searches need not worry about it
newSet.insert(j->first);
j = sortedInput.erase(j);
}
else
{
// else, next element
++j;
}
}
// If size is bigger than our original push, we have a match
// - save it to the output
if(newSet.size() > 1)
{
output.push_back(newSet);
}
// erase this element and update the iterator
i = sortedInput.erase(i);
}
}
在回顾评论并学习更多内容之后,这是第二遍:
void doBetterWork(list input, list> &output)
{
typedef std::multimap< string, string > SortedInputType;
SortedInputType sortedInput;
vector< string > sortedNames;
for(vector< string >::iterator i = input.begin(); i != input.end(); ++i)
{
string tempString(*i);
std::sort(tempString.begin(), tempString.end());
sortedInput.insert(make_pair(tempString, *i));
sortedNames.push_back(tempString);
}
for(list< string >::iterator i = sortedNames.begin(); i != sortedNames.end(); ++i)
{
pair< SortedInputType::iterator,SortedInputType::iterator > bounds;
bounds = sortedInput.equal_range(*i);
set< string > newSet;
for(SortedInputType::iterator j = bounds.first; j != bounds.second; ++j)
{
newSet.insert(j->second);
}
if(newSet.size() > 1)
{
output.push_back(newSet);
}
sortedInput.erase(bounds.first, bounds.second);
}
}
答案 0 :(得分:14)
将字谜分组的最佳方法是将字符串映射到某种直方图表示。
FUNCTION histogram
[input] -> [output]
"dog" -> (1xd, 1xg, 1xo)
"god" -> (1xd, 1xg, 1xo)
"foo" -> (1xf, 2xo)
基本上,通过线性扫描字符串,您可以生成直方图表示它包含的每个字母的数量。一个小的,有限的字母表使这更容易(例如A-Z
,你只有一个26个数字的数组,每个字母一个)。
现在,anagrams只是具有相同直方图的单词。
然后你可以有一个多图数据结构,将直方图映射到具有该直方图的单词列表。
MULTIMAP
[key] => [set of values]
(1xd, 1xg, 1xo) => { "dog", "god" }
(1xf, 2xo) => { "foo" }
您还可以处理字符串的"canonical form",而不是处理直方图。基本上,你为每个字符串定义它的规范形式,如果它们具有相同的规范形式,则两个单词是字谜。
一种方便的规范形式是按字母顺序排列字符串中的字母。
FUNCTION canonize
[input] -> [output]
"dog" -> "dgo"
"god" -> "dgo"
"abracadabra" -> "aaaaabbcdrr"
请注意,这只是直方图方法之后的一步:您实际上正在对counting sort进行排序。
对于您的问题,这是实际编程语言中最实用的解决方案。
生成单词的直方图/规范形式实际上是O(1)
(有限字母大小,有限最大字长)。
通过良好的哈希实现,多图上的get
和put
为O(1)
。
您甚至可以拥有多个多重映射,每个字长一个。
如果有N
个字词,那么将所有字词放入多个地图中是O(N)
;然后输出每个anagram组只是将值转储到多图中。这也可以在O(N)
中完成。
这肯定比检查每对单词是否为anagrams(O(N^2)
算法)更好。
答案 1 :(得分:1)
我会将其视为免费功能get_anagrams
,因为class Anagram
似乎没有做任何其他事情。
list
和set
的选择并不比其他任何东西都好,所以当我接触它时,我会将它们都vector
。实际上,输出可以只是一个平坦的序列。所以称之为anagram_sort
。我将“list”和“set”的规范作为基本构造而不是文字类模板。 “给定...返回”也可以松散地解释为就地修改输入。如果他们愿意,调用者可以创建一个副本。
struct anagram_less { bool operator()( string const &l, string const &r ) {
string sl( l ), sr( r );
sort( sl.begin(), sl.end() );
sort( sr.begin(), sr.end() );
return sl < sr;
} };
void anagram_sort( vector< string > &v ) { // "the answer"
sort( v.begin(), v.end(), anagram_less );
} // 10 lines
// usage:
void print_anagrams( vector< string > &&v ) { // destructive => use rvalue ref
anagram_sort( v );
for ( vector< string >::iterator i = v.begin(); i != v.end(); /*++i*/ ) {
vector< string >::iterator e // find end of run of anagrams
= adjacent_find( i, v.end(), anagram_less() );
if ( e != v.end() ) ++ e; // usually need this after adjacent_find :v(
cout << "( ";
for ( ; i != e; ++ i ) {
cout << * i << " ";
}
cout << ")" << endl;
}
}
这是次要的,因为它重复地对单词进行排序。我宁愿在一个面试问题中削减脂肪,也不愿做出“纸上谈兵”的事情。
为了挤出最后一点性能,我可能会复制带有序列号索引的输入,对字符串的字符进行排序,对字符串进行排序,然后使用索引对输入向量进行重新排序使用this algorithm。最终运行时应该是低系数O(n log n)。
答案 2 :(得分:1)
检查两个字符串是否是彼此的字谜的算法如下
将这两个字符串转换成只包含字母(因为婆婆和女性希特勒也是字谜)。还要使两个字符串相等的情况意味着两个字符串都是大写的或小写。
现在对两个字符串中的字符进行排序。
比较两个字符串是否相等意味着它们是彼此的字谜
这是这个算法的代码
bool checkanagrams(string first,string second)
{
string tempfirst,tempsecond;
int countfirst = 0;
int countsecond = 0;
// only storing characters removing other junk things like -
for(int i=0;i<first.length();i++)
{
if(isalpha(first[i])
{
tempfirst[countfirst] =first[i];
countfirst++;
}
}
for(int i=0;i<second.length();i++)
{
if(isalpha(second[i])
{
tempsecond[countsecond] =second[i];
countsecond++;
}
}
sort(tempfirst.begin(),tempfirst.end());
sort(tempsecond.begin(),tempsecond.end());
if(!strcmp(tempfirst.c_str(),tempsecond.c_str())
return true;
else
return false;
}
答案 3 :(得分:0)
我会将所有内容都放在哈希表中。然后对于每个字符串,我将其还原并检查它是否存在于哈希表中,如果是这样,则返回反向字符串及其自身的集合。
这种方法也可以在一组中找到回文。