【问题标题】:are two anagram or not? [closed]是两个字谜吗? [关闭]
【发布时间】:2021-07-30 09:47:47
【问题描述】:

我的问题是,在这段代码中,最初我们将布尔 isAnagram 设为 false,然后设置条件,但我们得到了错误的结果。很明显,它们不是字谜,但代码输出是'字谜'。

package strings;

public class Anagrams {

    public static void main(String[] args) {
        
        String a = "aab";
        String b = "abc";
        
        boolean isAnagram = false;
        
        int al[] = new int[256];
        int bl[] = new int[256];
        
        for(char c:a.toCharArray()) {
            int index = (int)c;
            al[index]++;
            }
        for(char c:b.toCharArray()) {
            int index = (int)c;
            bl[index]++;
            }
        
        for(int i = 0; i<256; i++) {
            if(al[i] == bl[i]) {
                isAnagram = true;
            }
        }
        
        if(isAnagram) {
            System.out.println("anagram");
        }else {
            System.out.println("not anagram");
        }
    
        }
    }
}

【问题讨论】:

  • 问题出在最后一个for-loop 中。我们必须遍历两个数组 ab 来确定这些词是否是彼此的字谜。因此,我建议从假设它们是字谜 (boolean isAnagram = true) 开始,遍历 ab 并将 isAnagram 设置为 false iff。 a[i] != b[i](如果发现不匹配,我们也可以关闭循环)。我也推荐阅读:How to debug small programs
  • 备注:chars 在java中实际上是用unicode编码的。所以charint 值可能是&gt;= 256 (Ideone demo)。承认,这可能是一个极端情况。但是使用Map&lt;Character, Integer&gt; 而不是int[256] 来跟踪字符出现可以解决这个问题。

标签: java string anagram


【解决方案1】:

我认为对字符串进行排序然后比较它们更简单。

public static void main(String[] args) {

    String a = "aab";
    String b = "abc";

    char[] a1 = a.toLowerCase().toCharArray();
    char[] b1 = b.toLowerCase().toCharArray();

    Arrays.sort(a1);
    Arrays.sort(b1);
    
    boolean isAnagram = new String(a1).equals(new String(b1));

    System.out.println(isAnagram ? "anagram" : "not anagram");
}

【讨论】:

  • 虽然你说得有道理,但它实际上并不能解释 OP 的问题。
  • @Stultuske 你是对的 ;-) 但我回答了标题中的问题。
  • 此外,排序通常具有更高的时间复杂度(O(n log(n)) 比线性扫描。
  • @Turing85 你强调优化的重要性。如果提问者提出要求,我自己会赞成你的回答。我没有在这里对他们投反对票。问候。
  • @bimjhi 不,不是。我只是指出这一点,因为原始解决方案具有线性时间复杂度。我的答案也有O(n log(n)) 的最坏情况时间复杂度。
【解决方案2】:

好的。

提问者希望他自己的算法能够工作。 主要的错误是它需要在字符集中找到不匹配的两个单词。

因此,您可以声明一个计数器,并在遍历两个单词中的 char 位置时,每次发现第一个和第二个单词中某些特定字母的数量不匹配时,都会增加计数器。

最后,如果计数器 > 0,这意味着单词有不同的字符集。

工作代码:

class Ideone
{
// Online Java Compiler
// Use this editor to write, compile and run your Java code online


public static void main(String[] args) {
        
        String a = "aab";
        String b = "abb";
        int mismatch = 0;
        
        boolean isAnagram = true;
        
        int al[] = new int[143859];
        int bl[] = new int[143859];
        
        for(char c:a.toCharArray()) {
            int index = (int)c;
            al[index]++;
            }
        for(char c:b.toCharArray()) {
            int index = (int)c;
            bl[index]++;
            }
        
        for(int i = 0; i<143859; i++) {
            if(al[i] != bl[i]) {
                mismatch++;
            }
        }
        
        if (mismatch>0) isAnagram = false;
        
        if(isAnagram) {
            System.out.println("anagram");
        }else {
            System.out.println("not anagram");
        }
    
        }

}

【讨论】:

  • 143859 是当前标准版本中 Unicode 字符的个数。
  • 如果al[i] != bl[i]为什么不中断?
  • 对人类可读性更好。您会看到代码查找不匹配并理解为什么最后会得到 isAnagram = false。我不否认您提倡的优化。
  • 这值得商榷。在我看来,你的算法计算了一些它不需要计算的东西(即不匹配的数量)。
【解决方案3】:

如果一个字符计数匹配,您的代码将生成 true。但如果所有字符计数都匹配,则它应该只是true。翻转逻辑,从true 开始,在第一个不匹配时设置为false。换行

boolean isAnagram = false;

boolean isAnagram = true;

if(al[i] == bl[i]) {
    isAnagram = true;
}

if(al[i] != bl[i]) {
    isAnagram = false;
    break;
}

但是对字符串进行排序确实是更具可读性和易于理解的解决方案。

【讨论】:

    【解决方案4】:

    问题是最后一个for-loop:

    for(int i = 0; i<256; i++) {
        if(al[i] == bl[i]) {
            isAnagram = true;
        }
    }
    

    如果两个数组中只有一个位置匹配,则isAnagram 设置为true。为了解决这个问题,我们可以颠倒我们的观点:让我们假设两个Strings 是开头的字谜(boolean isAnagram = true;)并将标志设置为false iff。两个数组ab 在某些索引i 上不同。我们也可以在我们发现的第一个不匹配处打破循环。

    public static void main(String[] args) {
      String a = "aab";
      String b = "aac";
    
      boolean isAnagram = true;
    
      int al[] = new int[256];
      int bl[] = new int[256];
    
      for (char c : a.toCharArray()) {
        int index = (int) c;
        al[index]++;
      }
      for (char c : b.toCharArray()) {
        int index = (int) c;
        bl[index]++;
      }
    
      for (int i = 0; i < 256; i++) {
        if (al[i] != bl[i]) {
          isAnagram = false;
          break;
        }
      }
    
      if (isAnagram) {
        System.out.println("anagram");
      } else {
        System.out.println("not anagram");
      }
    }
    

    Ideone demo

    由于 Java 中的 chars 以​​ unicode 编码,因此 charint 值可能是 &gt;= 256 (Ideone demo)。为了防止这个问题,我们可以使用Map&lt;Integer, Integer&gt; 来跟踪代码点频率:

    public static boolean areAnagrams(String s, String t) {
      Objects.requireNonNull(s, "Parameter \"s\" is null");
      Objects.requireNonNull(t, "Parameter \"t\" is null");
      return Objects.equals(s, t) ||
          Objects.equals(getCodePointFrequency(s), getCodePointFrequency(t));
    }
    
    public static Map<Integer, Integer> getCodePointFrequency(String s) {
      return s.codePoints()
          .boxed()
          .collect(Collectors.toMap(Function.identity(), c -> 1, Integer::sum));
    }
    

    Ideone demo

    应该提到的是,这个解决方案的最坏情况时间复杂度为O(n log(n)),因为插入到映射中只能保证O(log(n)),而不是O(1)。然而,平均情况应该是O(max(n))nst 中较长的String 的长度。

    【讨论】:

    • 为什么不把 256 换成 150k 呢?
    • 反问:为什么我应该分配比必要更多的内存?
    • 我们应该首先优化代码以提高人类可读性,并且只有在用户的性能成本超过代码读者的简洁设计成本时才针对机器进行优化。 (罗伯特 C. 马丁)
    • ant an int[](带有两个隐式映射)比 Map&lt;Character, Integer&gt;(带有一个显式映射)更具可读性?
    • 有人可能会反对 Map 的解决方案是特定于平台的。比如说,C# 开发人员必须查看 Oracle 文档中的 Collectors 类描述。
    猜你喜欢
    • 2019-05-02
    • 2018-09-22
    • 2013-11-25
    • 2016-04-07
    • 1970-01-01
    • 2014-09-03
    • 2011-10-01
    • 2013-01-22
    相关资源
    最近更新 更多