【问题标题】:Given a word and a text, we need to return the occurrences of anagrams给定一个单词和一个文本,我们需要返回字谜的出现
【发布时间】:2013-09-15 10:49:20
【问题描述】:

给定一个单词和一个文本,返回文本中单词的字谜出现次数。 例如。单词是“for”,文本是“forxxorfxdofr”,“for”的变位词将是“ofr”、“orf”、“fro”等。因此对于这个特定示例,答案是 3。

我有蛮力方法,它获取单词的所有排列,然后比较文本是否包含它,并增加出现次数,但这是 O(N^2) 方法。我正在寻找更好的复杂性。

【问题讨论】:

  • 你是不是故意在问题的标题中写了“wrod”?
  • @Rémi 已修复,感谢您的指出。
  • 这很有趣,你应该离开它 :-)
  • 在本文中forxxorfxdofr x 是分隔符吗?
  • forf 的预期结果是什么? 2 或 1。

标签: c++ algorithm


【解决方案1】:

您可以简单地查找字符数。

例如,假设您正在寻找 look 的字谜。所以,您正在寻找:

  • 一个 4 字符长度的单词,
  • 有 1 l、2 o 和 1 k。

只需处理前 4 个字母,存储计数。检查是否有匹配项。 添加下一个字符(增量),删除旧字符(减量)。再检查一遍。 等等……

【讨论】:

  • +1 提供清晰简洁的想法,而无需交出完整的作业解决方案。
  • @us2012 这不是作业解决方案,这是亚马逊的面试问题,我只是在想它以获得更好的解决方案。
  • @Karoly 存储每个字符的计数将在例如哈希表中?否则它仍然是 O(N^2)
  • 如果您可以假设 ASCII 字符集和 8 位 chars 等,您可以“作弊”并拥有一个长度为 256 的向量/数组以供出现。否则,是的,hashmap std::unordered_map 或类似的听起来不错。
  • 在这种情况下,您的搜索长度为 3。所以你会到达for,然后你会到达orf。而且我也不管你怎么存储,只要访问是O(1),整体算法是O(N);)
【解决方案2】:

TooTone's O(n) solution 不得不为输入文本的每个字符比较两个 256 元素向量。这可以通过跟踪两个向量不同的位置的数量来避免,并在该数量变为零时记录匹配。事实上,我们甚至根本不需要存储两个不同的向量,因为我们可以只存储一个包含它们差异的向量。

这是我实现这些优化的版本。它是用普通的旧 C 语言编写的,但在经过适当调整后应该可以在 C++ 下工作:

#include <stdio.h>
#include <limits.h> /* for UCHAR_MAX (usually 255) */

int find_anagrams (char *word, char *text) {
    int len = 0;           /* length of search word */
    int bin[UCHAR_MAX+1];  /* excess count of each char in last len chars of text */
    int mismatch = 0;      /* count of nonzero values in bins[] */
    int found = 0;         /* number of anagrams found */
    int i;                 /* generic loop counter */

    /* initialize bins */
    for (i = 0; i <= UCHAR_MAX; i++) bin[i] = 0;
    for (i = 0; word[i] != '\0'; i++) {
        unsigned char c = (unsigned char) word[i];
        if (bin[c] == 0) mismatch++;
        bin[c]--;
        len++;  /* who needs strlen()? */
    }

    /* iterate through text */
    for (i = 0; text[i] != '\0'; i++) {
        /* add next char in text to bins, keep track of mismatch count */
        unsigned char c = (unsigned char) text[i];
        if (bin[c] == 0) mismatch++;
        if (bin[c] == -1) mismatch--;
        bin[c]++;

        /* remove len-th previous char from bins, keep track of mismatch count */
        if (i >= len) {
            unsigned char d = (unsigned char) text[i - len];
            if (bin[d] == 0) mismatch++;
            if (bin[d] == 1) mismatch--;
            bin[d]--;
        }

        /* if mismatch count is zero, we've found an anagram */
        if (mismatch == 0) {
            found++;
#ifdef DEBUG
            /* optional: print each anagram found */
            printf("Anagram found at position %d: \"", i-len+1);
            fwrite(text+i-len+1, 1, len, stdout);
            printf("\"\n");
#endif
        }
    }
    return found;
}


int main (int argc, char *argv[]) {
    if (argc == 3) {
        int n = find_anagrams(argv[1], argv[2]);
        printf("Found %d anagrams of \"%s\" in \"%s\".\n", n, argv[1], argv[2]);
        return 0;
    } else {
        fprintf(stderr, "Usage: %s <word> <text>\n", (argc ? argv[0] : "countanagrams"));
        return 1;
    }
}

【讨论】:

  • 不错的一个。在生产代码中,我会将vectors 缩小到26 长,甚至可能使用maps,但是通过使用不匹配的字母计数来完全避免比较的想法真的很甜蜜!
【解决方案3】:

基本上,您可以在输入内容上滑动一个单词长度的窗口,并计算窗口中每个字母的数量。当滑动窗口中的字母计数与单词的字母计数匹配时,您就有了匹配项。

让您的字长为n,您的当前位置为curr。创建一个长度为 26 的数组或 vectorwindCounts。条目 windCounts[i] 存储从位置 curr - n - 1 到 @987654327 看到的字母表中第 ith 个字母的出现次数@。

您所做的是推进curr,并通过减少从滑动窗口后面掉出的字母并增加出现在滑动窗口的前面。 (很明显直到curr > n,你只会增加,你只是建立你的滑动窗口到你的单词长度。)

在 C++ 中,您可以使用 vector 来计算单词中的字母数,以及滑动窗口中的字母数,然后简单地使用 vector::operator== 进行相等。

编辑:算法为O(N),其中N 是要搜索的文本长度。这可以从下面的代码中看出,其中为您滑动窗口的每个字母执行循环体。

#include <string>
#include <vector>
#include <algorithm> // for_each 

using std::string;
using std::vector;

#include <iostream>

int main(int argc, char* argv[])
{
    const string text = "forxxorfxdofr";
    const string word = "for"; 

    // Counts of letters in word
    vector<int> wordCounts(256); // optimization: cut down from 256 to 26 
    std::for_each(word.begin(), word.end(), 
        [&] (char c) { wordCounts[c]++; } );

    // Current position of end of sliding window
    string::const_iterator curr = text.begin() + word.size();
    // Initial sliding window counts
    vector<int> windCounts(256);
    std::for_each(text.begin(), curr,
        [&] (char c) { windCounts[c]++; } );

    // Run sliding window over text
    int numMatches = 0;
    while (1) {
        numMatches += wordCounts == windCounts;
        if (curr == text.end()) {
            break;
        }
        windCounts[*(curr - word.size())]--;
        windCounts[*curr]++;
        ++curr;
    }

    std::cout << numMatches << "\n";

    return 0;
}

【讨论】:

  • 复杂性:O(#alphabet #text)。 (#alphabet 可以看作是一个常数)。
  • @Jarod42 谢谢。正如您所说,字母表的大小是一个常数。你是对的,我没有正确更新计数。有几个错误:(我只测试了原始问题的原始代码。我做了更多的测试,今天晚些时候会做更多的测试。再次感谢。
【解决方案4】:

我取了两个字符串,即str和occ。 Str 是原始字符串,occ 是我们必须找出计数的字符串。使用 strncpy 函数,我已将 occ 的长度(即 n 个字符)复制到临时数组中,然后检查它是否是 occ 字符串的排列。

#include<iostream.h>  
#include<conio.h>  
#include<string.h>

int permutate(char str1[],char str2[]);  
int permutate(char str1[],char str2[]) {  
    int c[256]={0},i,j;  
    for(i=0;i<strlen(str1);i++)  
        c[str1[i]]++;      

    for(i=0;i<strlen(str2);i++) {  
        c[str2[i]]--;      
        if(c[str2[i]]<0)   
            return 1;   //not a permutation       
    }  
    return 0;           //permutation   
}  

int main()  {
    //enter code here  
    char str[]="forxxorfxdofr",occ[]="for",temp[10];
    int n,i,x,t=0;   
    n=strlen(occ);
    for(i=0;i<strlen(str);i++) {
        strncpy(temp,str+i,n);    //copy the n char from str to temp
        x=permutate(temp,occ);
        if(x==0)                  //if string is a permutation
            t++;
    }
    cout<<"Count = " << t;
    return 0;
}  

【讨论】:

    【解决方案5】:
    o(n) solution in Python 
    

    def 检查(s1,s2):

    function takes in s1 as the text and s2 as the text to be checked from here for
    
    
    c=0
    n=len(s2)
    ck=sorted(s2)
    mk=''.join(ck)
    
    
     this loop will pick from s till the length of s2 that is 'for'
    
    
    
    for i,item in enumerate(s1):
        if s1[i] in mk:
            p=s1[i:i+n]
            jk=sorted(p)
            er=''.join(jk)
    
    now just comparing the both sorted strings if they are equal then they were anagram 
    
            if er == mk:
                c+=1
    return c
    

    【讨论】:

      猜你喜欢
      • 2013-10-10
      • 1970-01-01
      • 1970-01-01
      • 2021-02-14
      • 2019-06-14
      • 2011-06-04
      • 2021-08-23
      • 1970-01-01
      • 2019-04-26
      相关资源
      最近更新 更多