8

我最近被要求设计一种算法来检查两个字符串是否是彼此的字谜。我的目标是最小化空间和时间复杂度,所以我想出了这个算法:

  1. 创建一个包含 26 个元素的数组,每个元素都初始化为零。
  2. 遍历第一个字符串并为每个字符递增对应于该字符的数组元素。
  3. 遍历第二个字符串,并为每个字符递减对应于该字符的数组元素。
  4. 扫描阵列。如果所有元素都为 0,则这两个字符串是字谜。

但是,这个算法的时间复杂度是 O(n),我想不出一个复杂度更低的算法。有人知道吗?

4

5 回答 5

15

您的算法是渐近最优的。不可能在 Ω(n) 时间内更好地解决这个问题。为了看到这一点,假设存在可以在 o(n) 时间内解决问题的算法 A(请注意,这里是 n 的 little-o)。那么对于任何 1 > ε > 0,有一些 n 使得对于任何大小至少为 n 的输入,算法必须在最多 εn 步中终止。设置 ε = 1/3 并考虑算法的任何输入,对于前面提到的这个 ε,长度至少为 n。由于该算法最多只能查看两个字符串中 1/3 的字符,因此该函数必须有两个不同的输入,一个是一对字谜,一个不是,这样算法就可以查看每个输入的字符的相同子集。然后该函数必须在每种情况下产生相同的输出,因此至少在其中一个输入上是错误的。我们遇到了一个矛盾,所以这样的算法一定不存在。

于 2011-03-29T09:02:35.213 回答
3

您可以通过提前退出来提高平均性能。在扫描第二个字符串时,如果在递减之前 count[char] 为 0,则您没有字谜,您可以停止扫描。

此外,如果字符串短于 26 个字符,则在最后一步中,仅检查第一个字符串中的字符是否为零。

这不会改变大 O,但它可以将您的平均运行时间更改为小于建议解决方案的 2N+26 o,具体取决于您的数据。

于 2011-08-20T23:47:15.830 回答
1

为了确保字符串是字谜,您需要比较整个字符串 - 那么这怎么可能比 o(n) 快?

于 2011-03-29T09:00:13.437 回答
1

让我们提出一个问题:给定两个字符串 s 和 t,编写一个函数来确定 t 是否是 s 的字谜。

例如,s = "anagram",t = "nagaram",返回 true。s =“老鼠”,t =“汽车”,返回假。

方法一(使用 HashMap ):

    public class Method1 {

    public static void main(String[] args) {
        String a = "protijayi";
        String b = "jayiproti";
        System.out.println(isAnagram(a, b ));// output => true

    }

    private static boolean isAnagram(String a, String b) {
        Map<Character ,Integer> map = new HashMap<>();
        for( char c : a.toCharArray()) {
            map.put(c,    map.getOrDefault(c, 0 ) + 1 );
        }
        for(char c : b.toCharArray()) {
            int count = map.getOrDefault(c, 0);
            if(count  == 0 ) {return false ; }
            else {map.put(c, count - 1 ) ; }
        }

        return true;
    }

}

方法二:

    public class Method2 {
public static void main(String[] args) {
    String a = "protijayi";
    String b = "jayiproti";


    System.out.println(isAnagram(a, b));// output=> true
}

private static boolean isAnagram(String a, String b) {


    int[] alphabet = new int[26];
    for(int i = 0 ; i < a.length() ;i++) {
         alphabet[a.charAt(i) - 'a']++ ;
    }
    for (int i = 0; i < b.length(); i++) {
         alphabet[b.charAt(i) - 'a']-- ;
    }

    for(  int w :  alphabet ) {
         if(w != 0 ) {return false;}
    }
    return true;

}
}

方法3:

    public class Method3 {
public static void main(String[] args) {
    String a = "protijayi";
    String b = "jayiproti";


    System.out.println(isAnagram(a, b ));// output => true
}

private static boolean isAnagram(String a, String b) {
    char[] ca = a.toCharArray() ;
    char[] cb = b.toCharArray();
    Arrays.sort(   ca     );

    Arrays.sort(   cb        );
    return Arrays.equals(ca , cb );
}
}

方法四:

    public class Method4 {
public static void main(String[] args) {
    String a = "protijayi";
    String b = "jayiproti";
    //String c = "gini";

    System.out.println(isAnagram(a, b ));// output => true
}

private static boolean isAnagram(String a, String b) {
    Map<Integer, Integer> map = new HashMap<>();
    a.codePoints().forEach(code -> map.put(code, map.getOrDefault(code, 0) + 1));
    b.codePoints().forEach(code -> map.put(code, map.getOrDefault(code, 0) - 1));
    //System.out.println(map.values());

    for(int count : map.values()) {
       if (count<0) return false;

    }

    return true;
}
}
于 2018-03-31T15:15:49.897 回答
-2
int anagram (char a[], char b[]) {

  char chars[26];
  int ana = 0;
  int i =0;

  for (i=0; i<26;i++)
        chars[i] = 0;


  if (strlen(a) != strlen(b))
        return -1;

  i = 0;
  while ((a[i] != '\0') || (b[i] != '\0')) {
        chars[a[i] - 'a']++;
        chars[b[i] - 'a']--;
        i++;
  }

  for (i=0; i<26;i++)
        ana += chars[i];

   return ana;

}


void main() {

  char *a = "chimmy\0";
  char *b = "yimmch\0";

  printf ("Anagram result is %d.\n", anagram(a,b));


}
于 2013-04-23T18:17:43.527 回答