【问题标题】:Find if 2 strings are anagram in O(1) space and O(n) time在 O(1) 空间和 O(n) 时间中查找 2 个字符串是否是字谜
【发布时间】:2011-10-02 12:42:40
【问题描述】:

在 O(nlogn) 时间内对两个字符串进行排序后,您可以找到 2 个字符串是否是字谜,但是是否有可能在 o(n) 时间和 O(1) 空间中找到它。

【问题讨论】:

    标签: algorithm anagram


    【解决方案1】:

    这里绝对没有专家......

    但是为什么不检查每个字符串并简单地计算每个字母出现的次数。

    如果实施得当,这不会超过 O(n) 时间。

    【讨论】:

    • 好吧,如果它只使用字母,那么它会占用 O(1) 空间,因为有 26 个字母,而 26 = O(1)。 ;)
    • +1 为了快速完成,我将存储 2 个 int[256] 数组,或者如果您不限于扩展 ASCII,则 int[#] 其中 # 是您的输入字符集的大小.使用单个 unsigned int 单步执行并在字符的代码点处增加 int 的值(将其转换为 int)。然后运行两个数组并确保每个值都相等。如果没有,则立即返回 NO,如果到达终点,则返回 YES。
    • @Petar,你提出了一个很好的观点,如果我们有一种包含多种字母类型的外星语言怎么办……我们迷路了!
    • @shreyasva:字符集是不变的。很公平,它不是一个小数组,但 256 个整数在您的输入大小(即您要比较的字符串的长度)方面将保持不变。所以它仍然是 O(1) 空间,因为即使你使用一个巨大的输入字符串,一个 int 总是占用相同数量的空间。
    • @darvids0n 从技术上讲,不-您的整数是固定长度的,因此足够长的输入字符串将需要更多存储空间来存储计数,使其成为 O(n log m)。但我个人认为,这种区别太过分了,不值得做。 ;)
    【解决方案2】:

    生成一个素数数组[26] 每个素数代表一个字符,然后在遍历字符串时,将每个字符的素数相乘,如果相等,则为字谜,否则不。它需要 O(n) 和恒定空间

    【讨论】:

      【解决方案3】:

      有几种方法可以解决它。

      方法一 - 使用自定义哈希码函数
      我们可以有 hashCode 函数,如:

      static int[] primes = {3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103};
      static String alphabet = "abcdefghijklmnopqrstuvwxyz";
      
      
      public static int hashCode(String s){
          int sum = 0;
      
          for(char c: s.toCharArray()){
            sum += primes[c-97];
          }
          return sum;
      }
      

      生成两个字符串的哈希,如果 hashCode 相等,则字符串为字谜。这种方法类似于 Jin 提到的解决方案,因为它以某种方式为字符串生成 hashCode。
      时间复杂度 - O(n)

      方法2 - 使用字符和整数的hashmap
      将 2 个字符串视为 2 个字符数组。遍历第一个数组,将字符添加到char和count的hashmap中,找到字符后增加计数。同样遍历第二个数组,减少哈希图中的计数器,或者如果你没有找到字符,它们不是字谜。最后,当 map 包含所有字符并且计数为 0 时,那么 2 个字符串又是字谜。

      方法 3 - 使用计数数组(我的最爱)

      
      boolean are_anagrams(string1, string2){
       
          let counts = new int[26];
       
          for each char c in lower_case(string1)
              counts[(int)c]++
       
          for each char c in lower_case(string2)
              counts[(int)c]--
       
          for each int count in counts
              if count != 0
                  return false
       
          return true
      }
      

      您可以获得所有代码here

      【讨论】:

      • 我想知道如何使用方法 1 为“cat”和“tac”生成相同的哈希码。这听起来不像是一个选项。
      • @videoguy:我完全同意你的看法。我已从哈希码更正为自定义哈希码 :) 感谢您的帮助 :)
      【解决方案4】:

      是的,使用哈希并计算出现次数。如果最后,我们有一个非零数字,那么字符串不是字谜。

      let h => hash which maps letters to occurence_count (initialized to 0)
      
      for each letter l in string a
        h[l] = h[l] + 1
      end
      
      for each letter l in string b
        h[l] = h[l] - 1
      end
      
      for each key l in h 
        return false if h[l] != 0
      end
      
      return true
      

      这将在 O(n) + O(n) + c = O(n) 中运行。我们的哈希包含 26 个字母的点,每个点都有一个与之关联的整数。因此空间为 O(26) = O(1)

      [[Edit]],同上,但带有时间分析注释:

      let h => hash which maps letters to occurence_count (initialized to 0)
      
      #this loop runs n times
      for each letter l in string a
        #hash lookups / writes are constant time
        h[l] = h[l] + 1
      end
      #above function ran O(n) time
      
      for each letter l in string b
        h[l] = h[l] - 1
      end
      
      #runs in O(alphabet) = O(c) = constant-time
      for each key l in h 
        return false if h[l] != 0
      end
      
      return true
      

      【讨论】:

        【解决方案5】:

        运行:O(n) + O(n) = O(n)

        修复已用空间:O(256) = O(1)

        这是Java代码

        private static boolean isAnagramWithOneArray(String strFirst, String strSecond) {
            int[] charsCount = new int[256];
        
            if (strFirst != null && strSecond != null) {
                if (strFirst.length() != strSecond.length()) {
                    return false;
                }
                for (int i = 0; i < strFirst.length(); i++) {
                    charsCount[strFirst.charAt(i)]++;
                    charsCount[strSecond.charAt(i)]--;
                }
                for (int i = 0; i < charsCount.length; i++) {
                    if (charsCount[i] != 0) {
                        return false;
                    }
                }
                return true;
            } else {
                return (strFirst == null && strSecond == null);
            }
        }
        

        【讨论】:

          【解决方案6】:
          unsigned char CharacterCheck(char item)
          {
          
              if ((item >= 'A') && (item <= 'Z'))
                  return (item - 'A');
          
              if ((item >= 'a') && (item <= 'z'))
                  return ((item - ('a' - 'A')) - 'A');
          
              return -1;
          
          }
          
          unsigned char AnagramCheck6 (char * item1, char * item2)
          {
              char *test                      = item1;
              char *test2                     = item2;
              int count                       = 0;
              unsigned char rc                = 0;
              unsigned char rslt              = 0;
          
              while (*test && *test2)
              {
                  rslt = CharacterCheck(*test++);
          
                  if (rslt != 0xff)
                      count += rslt;
          
                  rslt = CharacterCheck(*test2++);
          
                  if (rslt != 0xff)
                      count -= rslt;
              }
          
              if (*test)
              {
                  while (*test)
                  {
                      rslt = CharacterCheck(*test++);
          
                      if (rslt != 0xff)
                          count += rslt;
                  }
              }
          
              if (*test2)
              {
                  while (*test2)
                  {
                      rslt = CharacterCheck(*test2++);
          
                      if (rslt != 0xff)
                          count -= rslt;
                  }
              }
          
              if (count)
                  rc = 1;
          
              return rc;
          
          }
          

          下面的 sn-p 检查正确的字符并在需要时转换大小写。第二次和第三次检查会考虑字符串是否长度不同

          【讨论】:

            【解决方案7】:

            如果您按排序顺序转换单词字符并对字符串进行哈希处理。排序后具有相同哈希的每个字符串将是另一个的字谜(很可能,总是有冲突的机会)。

            【讨论】:

              【解决方案8】:

              以上代码在所有情况下都无法正常工作

              我们可以进行快速排序并在消除空格的同时比较数组

              【讨论】:

                【解决方案9】:

                这里的所有建议都倾向于使用相同的方法对输入字符串进行排序,然后比较结果。对常规 ascii 字母最感兴趣,这可以通过计数排序来优化,这似乎是大多数回答者的方法。计数排序可以在 O(n) 中对有限的数字/整数字母表进行排序,因此从技术上讲它是正确的答案。如果我们必须考虑之后遍历计数数组的时间,它将包括字母表的时间,在字母表是 UTF-32 的情况下,使 O(m+n) 成为更正确的上限。

                我倾向于认为最普遍正确的方法需要 O(n lg n),因为如果字母表不能被充分限制,快速排序可能会实时更快。

                【讨论】:

                  【解决方案10】:

                  我会这样做:

                  //is s an anagram of t?
                  #include <string>
                  
                  bool is_anagram(const string& s, const string& t)
                      {
                      bool ret = false;
                  
                      //if s and t are anagrams, they must of same size
                      if( s.length() != t.length() )
                          {
                          return ret;
                          }
                  
                          //assume that s and t have valid ascii characters only
                      char letters[ 256 ] = {0};
                      int i;
                  
                      // find occurence of each character in string s
                      for( i = 0; i < s.length(); i++ )
                          {
                          (letters[ s[i] ])++;
                          }
                  
                      // now, find occurence of each character in string t, but decrement 
                      // correspnding character
                      for( i = 0; i < t.length(); i++ )
                          {
                          //if the count is <0 means the given character is not present in s
                          if( --(letters[ t[i] ]) < 0 ) 
                              {
                              return ret;
                              }
                          }
                  
                      //out of for loop means success
                      ret = true;
                      return ret;
                      }
                  

                  【讨论】:

                    【解决方案11】:

                    使用 python 做类似的事情?

                    def anagram(a,b):
                        s1=list(a)
                        s2=list(b)
                        for i in s1:
                            if i in s2:
                                s2.remove(i)
                        print(s2)
                        return len(s2)==0
                    

                    如果字符串不是彼此的字谜,这将返回 false。

                    为了避免使用 remove 等内置插件,可以使用以下方法吗?

                    def hana(a,b):
                        s1=list(a.lower())
                        s2=list(b.lower())
                        print(s1,s2)
                        h={'a': 0, 'c': 0, 'b': 0, 'e': 0, 'd': 0, 'g': 0, 'f': 0, 'i': 0, 'h': 0, 'k': 0,'j': 0, 'm': 0, 'l': 0, 'o': 0, 'n': 0, 'q': 0, 'p': 0, 's': 0, 'r': 0, 'u': 0, 't': 0, 'w': 0, 'v': 0, 'y': 0, 'x': 0, 'z': 0}
                        for i in s1:
                            h[i]+=1
                        for i in s2:
                            h[i]-=1
                        for key, val in h.items():
                            if val != 0:
                                return False
                        return(True)
                    

                    如果字符串是彼此的字谜,那么所有键的最终哈希值应该为 0。如果它不是 0,那么我们有重复的字符或其他字符串中不存在的字符,这意味着它不是 anagram。

                    【讨论】:

                    • 您能否详细说明问题规模越来越大的运行时? anagram('fallacies', "false")
                    【解决方案12】:

                    可能是这样的:

                        String str1 = "test";
                        String str2 = "tzet";
                        int count = 0;
                        for (int i = 0; i < str1.length(); i++)
                        {
                            count = count + str1.charAt(i) - str2.charAt(i);
                        }
                        System.out.println(count);
                    

                    从字符串 2 中减去每个字符,然后将字符串 1 中的每个字符相加到 count(假设为 ASCII 字符)。如果它们是字谜,则计数将等于零。

                    不过,这不包括插入空格的字谜。

                    【讨论】:

                    • str1=cccc str2=bdcc 打印:0
                    猜你喜欢
                    • 1970-01-01
                    • 2011-08-09
                    • 2016-02-16
                    • 2020-05-26
                    • 2012-08-06
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-08-14
                    • 2016-12-12
                    相关资源
                    最近更新 更多