过去在面试时检查我的字谜代码

时间:2010-04-25 03:31:20

标签: c++ anagram

前面有一个面试问题,并且在基本语法上窒息得太厉害,我没能推进(一旦肾上腺素开始,编码就会消失。)

给定一个字符串列表,返回一组字符串,这些字符串是输入集的字谜。即“狗”,“上帝”,“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); } }

4 个答案:

答案 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)(有限字母大小,有限最大字长)。

通过良好的哈希实现,多图上的getputO(1)

您甚至可以拥有多个多重映射,每个字长一个。

如果有N个字词,那么将所有字词放入多个地图中是O(N);然后输出每个anagram组只是将值转储到多图中。这也可以在O(N)中完成。

这肯定比检查每对单词是否为anagrams(O(N^2)算法)更好。

答案 1 :(得分:1)

我会将其视为免费功能get_anagrams,因为class Anagram似乎没有做任何其他事情。

listset的选择并不比其他任何东西都好,所以当我接触它时,我会将它们都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)

检查两个字符串是否是彼此的字谜的算法如下

  1. 将这两个字符串转换成只包含字母(因为婆婆和女性希特勒也是字谜)。还要使两个字符串相等的情况意味着两个字符串都是大写的或小写。

  2. 现在对两个字符串中的字符进行排序。

  3. 比较两个字符串是否相等意味着它们是彼此的字谜

  4. 这是这个算法的代码

    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)

我会将所有内容都放在哈希表中。然后对于每个字符串,我将其还原并检查它是否存在于哈希表中,如果是这样,则返回反向字符串及其自身的集合。

这种方法也可以在一组中找到回文。