2010-11-21 90 views
21

我正在寻找一种方法来查找两个字符串是否是另一个的字符串。查找两个单词是否是对方的字典

Ex: string1 - abcde 
string2 - abced 
Ans = true 
Ex: string1 - abcde 
string2 - abcfed 
Ans = false 

,我想出了这样的是两个字符串进行排序和每个字符由两个字符串,直到要么strings.It年底比较,将是O(LOGN)。我要寻找一些其他有效的解决方案被比较

+3

posssible重复[?什么是简单的方法来辨别单词列表互为字谜(HTTP://计算器。 com/questions/522112/what-is-easy-way-to-tell-if-a-list-of-words-are-anagrams-of-other) – kennytm 2010-11-21 07:53:20

+0

请注意,使用多种唯一排序用O(1)(不取决于字符集)堆栈内存使用的固定字符集的O(n)。当然,这仍然会修改这两个字符串。 – Kaganar 2013-07-02 21:43:06

回答

50

计算两个字符串中每个字符的频率。检查两个直方图是否匹配。 O(n)时间,O(1)空间(假设ASCII)(当然它仍然是O(1)空间的Unicode,但表格将变得非常大)。

+11

+1简单而有效。通过增加一个空间并递减另一个字符串并检查0,可以轻松地将空间减半。对于unicode,Hashmap方法似乎是合适的。 – Eiko 2010-11-21 07:52:31

+8

初始字符串长度检查有助于消除大量候选字符串。作为@Eiko提到的 – 2012-12-07 23:03:06

+0

我觉得它是正确的,但记住即使如果你认为散列表初始化在JAVA中使用一些默认值(如16)创建,只要表增加,它就会重新散列这是开销。现在为了避免这种情况,您可以创建初始容量的hashmap,就像创建该容量的数组一样,因为hashmap是数组。即使设置初始容量,我也会觉得基于字符的直接索引比计算哈希要好,但是每次将哈希映射到哈希映射中....如果我错了,请告诉我。 – MrA 2013-05-27 16:22:02

21
  1. 创建一个HashMap其中键不改变2串方法 -​​ 字母和值 - 信frequencey,
  2. 为第一串填充的散列映射(O(N))
  3. 为第二个字符串递减计数并从hashmap O中移除元素(n)
  4. 如果散列表为空,则该字符串不是字符串。
+0

不错的主意在第一次运行时递增,在第二次时递减。然而,这里不需要散列映射,因为这些键事先已知并可以用作数组索引。这将导致更快的实施。 – Philipp 2010-11-21 07:50:23

+3

@ Philipp如果我们正在处理一个unicode字符串,那么这可能是一个非常大的数组。 – meagar 2010-11-21 07:58:17

+1

如果你通常比较小字符串,这不是很浪费吗?如果字符串平均很小(就像大多数单词一样),那么只需将单词转换为字符数组,对数组进行排序,然后比较它们的相等性可能会比创建散列表的开销更快,执行增量&递减&然后检查散列表是否为空。 – 2013-05-24 05:01:36

1

那么你大概可以通过首先检查长度,然后对数字进行快速校验和来改进最好的情况和平均情况(不是复杂的,因为这可能会比排序更糟糕,序数值的总和),然后进行排序,然后进行比较。

如果字符串非常短,校验和开销与许多语言的排序没有太大差异。

0

我想你的排序算法不是真的O(log n),是吗?

您可以得到的最好的是O(n)为您的算法,因为您必须检查每个字符。

您可以使用两个表来存储每个字中每个字母的计数,用O(n)填充并与O(1)比较。

29

获取素数表格,足以将每个素数映射到每个字符。因此,从1开始,通过线,乘以代表当前字符的素数。你得到的数字只取决于字符串中的字符,而不取决于它们的顺序,并且每一组唯一的字符对应于唯一的数字,因为任何数字都可以用一种方式来分解。所以你可以比较两个数字来说明一个字符串是否是对方的字典。

不幸的是,你必须使用多个精度(任意精度)的整数算术来做到这一点,否则当你使用这种方法时你会得到溢出或舍入异常。
为此,您可以使用库如BigInteger,GMP, MPIRIntX

伪代码:

prime[] = {2, 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} 

primehash(string) 
    Y = 1; 
    foreach character in string 
     Y = Y * prime[character-'a'] 

    return Y 

isanagram(str1, str2) 
    return primehash(str1)==primehash(str2) 
+0

我不认为这会起作用。例如2 + 5 = 7,所以如果a = 2,b = 3,c = 5,d = 7,如果一个字符串有a和c,另一个有d,那么在你的“总和”中会有碰撞。如果你首先检查每个字符串的长度是否相同,也许这会成立? – runamok 2013-05-16 18:13:16

+3

素数相乘,未添加。 primehash(“ac”)= 2 * 5 = 10,primehash(“d”)= 7,所以它们不相等。由于数字与其主要因素之间存在一对一关系,所以不会发生碰撞。 – Vovanium 2013-06-01 12:47:34

+0

可爱 - 算术的基本定理! HTTPS://en.wikipedia。org/wiki/Fundamental_theorem_of_arithmetic – StackG 2015-07-29 12:48:03

1

这个怎么样?

 
a = "lai d" 
b = "di al" 
sorteda = [] 
sortedb = [] 
for i in a: 
    if i != " ": 
     sorteda.append(i) 
     if c == len(b): 
      for x in b: 
       c -= 1 
       if x != " ": 
        sortedb.append(x) 
sorteda.sort(key = str.lower) 
sortedb.sort(key = str.lower) 

print sortedb 
print sorteda 

print sortedb == sorteda 
+0

您在字符串中的选择可能会让您的攻击性标志足以让您的答案被删除。请考虑修改。我知道,但是,为了你们的例子,他们很方便。 – 2011-04-30 06:01:52

+0

好吧,我只是为了它认为是冒犯而改变了我的字谜! – AAF 2011-04-30 09:40:05

0

看来下面的实现也可以检查吗?

int histogram[256] = {0}; 
for (int i = 0; i < strlen(str1); ++i) { 
    /* Just inc and dec every char count and 
    * check the histogram against 0 in the 2nd loop */ 
    ++histo[str1[i]]; 
    --histo[str2[i]]; 
} 

for (int i = 0; i < 256; ++i) { 
    if (histo[i] != 0) 
    return 0; /* not an anagram */ 
} 

return 1; /* an anagram */ 
+0

为什么不能检查? – 2012-11-18 10:27:41

+0

这不是设定ASCII编码(不是为什么数组是256个元素宽)吗? – 2013-05-23 21:14:36

8

的步骤是:

  1. 检查的两个单词/字符串的长度是否相等,然后才能进行检查字谜别的什么也不做
  2. 排序两个单词/字符串然后比较

Java代码是相同的:

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 
package anagram; 

import java.io.BufferedReader; 
import java.io.IOException; 
import java.io.InputStreamReader; 
import java.util.Arrays; 

/** 
* 
* @author Sunshine 
*/ 
public class Anagram { 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) throws IOException { 
     // TODO code application logic here 
     System.out.println("Enter the first string"); 
     BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
     String s1 = br.readLine().toLowerCase(); 
     System.out.println("Enter the Second string"); 
     BufferedReader br2 = new BufferedReader(new InputStreamReader(System.in)); 
     String s2 = br2.readLine().toLowerCase(); 
     char c1[] = null; 
     char c2[] = null; 
     if (s1.length() == s2.length()) { 


      c1 = s1.toCharArray(); 
      c2 = s2.toCharArray(); 

      Arrays.sort(c1); 
      Arrays.sort(c2); 

      if (Arrays.equals(c1, c2)) { 
       System.out.println("Both strings are equal and hence they have anagram"); 
      } else { 
       System.out.println("Sorry No anagram in the strings entred"); 
      } 

     } else { 
      System.out.println("Sorry the string do not have anagram"); 
     } 
    } 
} 
1

Xor'ing这两个弦如何?这肯定会为O(n)的

char* arr1="ab cde"; 
int n1=strlen(arr1); 
char* arr2="edcb a"; 
int n2=strlen(arr2); 
// to check for anagram; 
int c=0; 
int i=0, j=0; 
if(n1!=n2) 
    printf("\nNot anagram"); 
else { 
    while(i<n1 || j<n2) 
    { 
     c^= ((int)arr1[i]^(int)arr2[j]); 
     i++; 
     j++; 
    } 
} 

if(c==0) { 
    printf("\nAnagram"); 
} 
else printf("\nNot anagram"); 

}

+4

我认为这种方法将不会始终工作。 考虑下面的例子: str1 =“aa”和str2 =“bb” 然后a^b^a^b(字符串的异或)将为零,但这些不是字谜! – 2013-01-12 18:01:46

+0

这不是一个解决方案 – siddhusingh 2013-07-16 09:41:55

0
/* Program to find the strings are anagram or not*/ 
/* Author Senthilkumar M*/ 

Eg. 
    Anagram: 
    str1 = stackoverflow 
    str2 = overflowstack 

    Not anagram:`enter code here` 
    str1 = stackforflow 
    str2 = stacknotflow 

int is_anagram(char *str1, char *str2) 
{ 
     int l1 = strlen(str1); 
     int l2 = strlen(str2); 
     int s1 = 0, s2 = 0; 
     int i = 0; 

     /* if both the string are not equal it is not anagram*/ 
     if(l1 != l2) { 
       return 0; 
     } 
     /* sum up the character in the strings 
      if the total sum of the two strings is not equal 
      it is not anagram */ 
     for(i = 0; i < l1; i++) { 
       s1 += str1[i]; 
       s2 += str2[i]; 
     } 
     if(s1 != s2) { 
       return 0; 
     } 
     return 1; 
} 
2

C#

public static bool AreAnagrams(string s1, string s2) 
{ 
    if (s1 == null) throw new ArgumentNullException("s1"); 
    if (s2 == null) throw new ArgumentNullException("s2"); 

    var chars = new Dictionary<char, int>(); 
    foreach (char c in s1) 
    { 
     if (!chars.ContainsKey(c)) 
      chars[c] = 0; 
     chars[c]++; 
    } 
    foreach (char c in s2) 
    { 
     if (!chars.ContainsKey(c)) 
      return false; 
     chars[c]--; 
    } 

    return chars.Values.All(i => i == 0); 
} 

一些测试:

[TestMethod] 
public void TestAnagrams() 
{ 
    Assert.IsTrue(StringUtil.AreAnagrams("anagramm", "nagaramm")); 
    Assert.IsTrue(StringUtil.AreAnagrams("anzagramm", "nagarzamm")); 
    Assert.IsTrue(StringUtil.AreAnagrams("anz121agramm", "nag12arz1amm")); 
    Assert.IsFalse(StringUtil.AreAnagrams("anagram", "nagaramm")); 
    Assert.IsFalse(StringUtil.AreAnagrams("nzagramm", "nagarzamm")); 
    Assert.IsFalse(StringUtil.AreAnagrams("anzagramm", "nag12arz1amm")); 
} 
-1

在Java中,我们也可以像这和它的非常简单的逻辑

import java.util.*; 

class Anagram 
{ 
public static void main(String args[]) throws Exception 
{ 
    Boolean FLAG=true; 

    Scanner sc= new Scanner(System.in); 

    System.out.println("Enter 1st string"); 

    String s1=sc.nextLine(); 

    System.out.println("Enter 2nd string"); 

    String s2=sc.nextLine(); 

    int i,j; 
    i=s1.length(); 
    j=s2.length(); 

    if(i==j) 
    { 
    for(int k=0;k<i;k++) 
    { 
    for(int l=0;l<i;l++) 
    { 
    if(s1.charAt(k)==s2.charAt(l)) 
    { 
     FLAG=true; 
     break; 
    } 
    else 
    FLAG=false; 
    } 
    } 
    } 
    else 
    FLAG=false; 
    if(FLAG) 
    System.out.println("Given Strings are anagrams"); 
    else 
    System.out.println("Given Strings are not anagrams"); 
} 
} 
+1

此代码有一个错误。它说12343和12344是anagrams,但它们不是。内部循环应该说明一个特定字符已经与第一个字符串进行比较的事实。 – lreeder 2013-07-04 15:50:12

0

如果两个字符串的长度相等,那么继续,如果不是,则字符串不是字母。

迭加每个字符串时,总结每个字符的序号。如果和数相等,那么这些字符串就是anagrams。

实施例:

public Boolean AreAnagrams(String inOne, String inTwo) { 

     bool result = false; 

     if(inOne.Length == inTwo.Length) { 

      int sumOne = 0; 
      int sumTwo = 0; 

      for(int i = 0; i < inOne.Length; i++) { 

       sumOne += (int)inOne[i]; 
       sumTwo += (int)inTwo[i]; 
      } 

      result = sumOne == sumTwo; 
     } 

     return result; 
    } 
+0

通常,在anagrams中会忽略空白区域。这意味着长度不是标准。一个着名的字谜:“威廉莎士比亚”是与“我是一个弱小的拼写者”的字谜。如果问题陈述没有说相反的话,我认为应该允许空格。 – 2013-12-18 21:47:50

1

对于已知(小)组有效字母(例如ASCII)的使用表具有与每个有效信相关联的计数。第一个字符串递增计数,第二个字符串递减计数。最后遍历整个表,看看所有计数是否为零(字符串是anagrams)还是非零值(字符串不是字母)。确保将所有字符转换为大写(或小写,完全相同)并忽略空格。

对于大量有效字母(如Unicode),请勿使用表格,而应使用散列表。它有O(1)次添加,查询和删除和O(n)空间。第一个字符串递增计数的字母,第二个字符串递减计数的字母。从哈希表中删除成为零的计数。如果末尾散列表为空,则字符串是anagrams。或者,只要任何计数变为负数,搜索就会以负面结果终止。

这里是C#的详细解释和执行:Testing If Two Strings are Anagrams

-1

如何转换成字符的int值和总结:

如果和的值都等于那么他们字谜每个其他。

def are_anagram1(s1, s2): 
    return [False, True][sum([ord(x) for x in s1]) == sum([ord(x) for x in s2])] 

s1 = 'james' 
s2 = 'amesj' 
print are_anagram1(s1,s2) 

该解决方案仅适用于'A'到'Z'和'a'到'z'。

+1

这不起作用。它为''ac''和''bb''返回'True'。 – 2014-09-06 17:27:07

1
static bool IsAnagram(string s1, string s2) 
     { 

      if (s1.Length != s2.Length) 
       return false; 
      else 
      { 
       int sum1 = 0; 
       for (int i = 0; i < s1.Length; i++) 
       sum1 += (int)s1[i]-(int)s2[i]; 
       if (sum1 == 0) 
        return true; 
       else 
        return false; 
      } 
     } 
+0

检查2个字符串是否为Anagram的最佳解决方案。 :) – user2605539 2014-03-16 17:44:35

+3

通过“'使'str1 =”az“'和'str2 =”。这种方法将会失败。 – Sinstein 2014-10-04 07:11:17

+0

由于算法不适用于所有输入,因此降低了投票率。请确保您在发布答案前正确检查所有解决方案。 – 2015-02-11 14:10:23

2

代码找到两个词是否字谜:

逻辑在几个答案已经解释和几个要求的代码。该解决方案在O(n)时间内产生结果。

此方法计算每个字符出现的次数并将其存储在每个字符串的相应ASCII位置中。然后比较两个数组的数量。如果不相等,给定的字符串不是字谜。

public boolean isAnagram(String str1, String str2) 
{ 
    //To get the no of occurrences of each character and store it in their ASCII location 
    int[] strCountArr1=getASCIICountArr(str1); 
    int[] strCountArr2=getASCIICountArr(str2); 

    //To Test whether the two arrays have the same count of characters. Array size 256 since ASCII 256 unique values 
    for(int i=0;i<256;i++) 
    { 
     if(strCountArr1[i]!=strCountArr2[i]) 
      return false; 
    } 
    return true; 
} 

public int[] getASCIICountArr(String str) 
{ 
    char c; 
    //Array size 256 for ASCII 
    int[] strCountArr=new int[256]; 
    for(int i=0;i<str.length();i++) 
    { 
     c=str.charAt(i); 
     c=Character.toUpperCase(c);// If both the cases are considered to be the same 
     strCountArr[(int)c]++; //To increment the count in the character's ASCII location 
    } 
    return strCountArr; 
} 
+0

@ S. H. - 我在这里添加了O(n)解决方案的代码 – Dany 2014-04-21 05:36:13

+0

OH !!!这就是“直方图”在这个特定情况下的含义。谢谢你。但是有一个问题,这个解决方案是O(N)空间的复杂性,最重要的评论说它是O(1)? – nyus2006 2014-04-30 23:18:23

+0

@ S.H.-所有字符串的数组大小为256。如果你给一个字符串中的一百万个字符,它仍然会存储在一个常数为256的数组中。所以复杂性是O(1)空间。 – Dany 2014-05-01 00:25:16

2

使用ASCII哈希映射,该哈希映射允许对每个字符进行O(1)查找。

上面列出的java示例正在转换为似乎不完整的小写字母。我在C的例子,简单地初始化散列映射阵列ASCII values到“-1”

如果字符串2的长度比串1不同,没有字谜

否则,我们更新相应的散列映射对于字符串1和字符串2中的每个字符,值为0

然后对于字符串1中的每个字符,我们更新哈希映射中的计数。类似地,我们递减string2中每个字符的计数值。

如果每个字符都是字母,结果应该将值设置为0。如果没有,通过字符串1设置了一些积极的值保持

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define ARRAYMAX 128 

#define True  1 
#define False  0 

int isAnagram(const char *string1, 
      const char *string2) { 

    int str1len = strlen(string1); 
    int str2len = strlen(string2); 

    if (str1len != str2len) /* Simple string length test */ 
     return False; 

    int * ascii_hashtbl = (int *) malloc((sizeof(int) * ARRAYMAX)); 
    if (ascii_hashtbl == NULL) { 
     fprintf(stderr, "Memory allocation failed\n"); 
     return -1; 
    } 
    memset((void *)ascii_hashtbl, -1, sizeof(int) * ARRAYMAX); 
    int index = 0; 
    while (index < str1len) { /* Populate hash_table for each ASCII value 
           in string1*/ 
     ascii_hashtbl[(int)string1[index]] = 0; 
     ascii_hashtbl[(int)string2[index]] = 0; 
     index++; 
    } 
    index = index - 1; 
    while (index >= 0) { 
     ascii_hashtbl[(int)string1[index]]++; /* Increment something */ 
     ascii_hashtbl[(int)string2[index]]--; /* Decrement something */ 
     index--; 
    } 
    /* Use hash_table to compare string2 */ 
    index = 0; 
    while (index < str1len) { 
     if (ascii_hashtbl[(int)string1[index]] != 0) { 
      /* some char is missing in string2 from string1 */ 
      free(ascii_hashtbl); 
      ascii_hashtbl = NULL; 
      return False; 
     } 
     index++; 
    } 
    free(ascii_hashtbl); 
    ascii_hashtbl = NULL; 
    return True; 
} 

int main() { 
    char array1[ARRAYMAX], array2[ARRAYMAX]; 
    int flag; 

    printf("Enter the string\n"); 
    fgets(array1, ARRAYMAX, stdin); 
    printf("Enter another string\n"); 
    fgets(array2, ARRAYMAX, stdin); 

    array1[strcspn(array1, "\r\n")] = 0; 
    array2[strcspn(array2, "\r\n")] = 0; 
    flag = isAnagram(array1, array2); 
    if (flag == 1) 
     printf("%s and %s are anagrams.\n", array1, array2); 
    else if (flag == 0) 
     printf("%s and %s are not anagrams.\n", array1, array2); 

    return 0; 
} 
1

如果字符串只有ASCII字符:

  1. 创建256长度
  2. 阵列横动阵列中的第一串和增量计数器index =字符的ascii值。当您到达字符串末尾时,还要继续计算字符以查找长度
  3. 遍历字符的index = ascii值处的第二个字符串并递减数组中的计数器。如果在递减前该值为0,则返回false,因为字符串不是字母。还要跟踪第二个字符串的长度。
  4. 在字符串遍历结束时,如果两者的长度相等,则返回true,否则返回false。

如果字符串可以有unicode字符,则使用哈希映射而不是数组来跟踪频率。算法的其余部分保持不变。

注:

  1. 计算长度,同时增加字符列阵确保我们遍历每个串一次。
  2. 仅在ASCII字符串的情况下使用数组根据需求优化空间。
0

实施斯威夫特3:

func areAnagrams(_ str1: String, _ str2: String) -> Bool { 
    return dictionaryMap(forString: str1) == dictionaryMap(forString: str2) 
} 

func dictionaryMap(forString str: String) -> [String : Int] { 
    var dict : [String : Int] = [:] 
    for var i in 0..<str.characters.count { 
     if let count = dict[str[i]] { 
      dict[str[i]] = count + 1 
     }else { 
      dict[str[i]] = 1 
     } 
    }   
    return dict 
} 
//To easily subscript characters 
extension String { 
    subscript(i: Int) -> String { 
     return String(self[index(startIndex, offsetBy: i)]) 
    } 
} 
0
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.LinkedHashMap; 
import java.util.Map; 
import java.util.Scanner; 

/** 
* -------------------------------------------------------------------------- 
* Finding Anagrams in the given dictionary. Anagrams are words that can be 
* formed from other words Ex :The word "words" can be formed using the word 
* "sword" 
* -------------------------------------------------------------------------- 
* Input : if choose option 2 first enter no of word want to compare second 
* enter word ex: 
* 
* Enter choice : 1:To use Test Cases 2: To give input 2 Enter the number of 
* words in dictionary 
* 6 
* viq 
* khan 
* zee 
* khan 
* am 
*  
* Dictionary : [ viq khan zee khan am] 
* Anagrams 1:[khan, khan] 
* 
*/ 
public class Anagrams { 

    public static void main(String args[]) { 
     // User Input or just use the testCases 
     int choice; 
     @SuppressWarnings("resource") 
     Scanner scan = new Scanner(System.in); 
     System.out.println("Enter choice : \n1:To use Test Cases 2: To give input"); 
     choice = scan.nextInt(); 
     switch (choice) { 
     case 1: 
      testCaseRunner(); 
      break; 
     case 2: 
      userInput(); 
     default: 
      break; 
     } 
    } 

    private static void userInput() { 
     @SuppressWarnings("resource") 
     Scanner scan = new Scanner(System.in); 
     System.out.println("Enter the number of words in dictionary"); 
     int number = scan.nextInt(); 
     String dictionary[] = new String[number]; 
     // 
     for (int i = 0; i < number; i++) { 
      dictionary[i] = scan.nextLine(); 
     } 
     printAnagramsIn(dictionary); 

    } 

    /** 
    * provides a some number of dictionary of words 
    */ 
    private static void testCaseRunner() { 

     String dictionary[][] = { { "abc", "cde", "asfs", "cba", "edcs", "name" }, 
       { "name", "mane", "string", "trings", "embe" } }; 
     for (int i = 0; i < dictionary.length; i++) { 
      printAnagramsIn(dictionary[i]); 
     } 
    } 

    /** 
    * Prints the set of anagrams found the give dictionary 
    * 
    * logic is sorting the characters in the given word and hashing them to the 
    * word. Data Structure: Hash[sortedChars] = word 
    */ 
    private static void printAnagramsIn(String[] dictionary) { 
     System.out.print("Dictionary : [");// + dictionary); 
     for (String each : dictionary) { 
      System.out.print(each + " "); 
     } 
     System.out.println("]"); 
     // 

     Map<String, ArrayList<String>> map = new LinkedHashMap<String, ArrayList<String>>(); 
     // review comment: naming convention: dictionary contains 'word' not 
     // 'each' 
     for (String each : dictionary) { 
      char[] sortedWord = each.toCharArray(); 
      // sort dic value 
      Arrays.sort(sortedWord); 
      //input word 
      String sortedString = new String(sortedWord); 
      // 
      ArrayList<String> list = new ArrayList<String>(); 
      if (map.keySet().contains(sortedString)) { 
       list = map.get(sortedString); 
      } 
      list.add(each); 
      map.put(sortedString, list); 
     } 
     // print anagram 
     int i = 1; 
     for (String each : map.keySet()) { 
      if (map.get(each).size() != 1) { 
       System.out.println("Anagrams " + i + ":" + map.get(each)); 
       i++; 
      } 
     } 
    } 
} 
相关问题