问题
I would like to make anagram algorithm but This code doesn't work. Where is my fault ? For example des and sed is anagram but output is not anagram Meanwhile I have to use string method. not array. :)
public static boolean isAnagram(String s1 , String s2)
{
String delStr="";
String newStr="";
for(int i=0;i<s1.length();i++)
{
for(int j=0 ; j < s2.length() ; j++)
{
if(s1.charAt(i)==s2.charAt(j))
{
delStr=s1.substring(i,i+1);
newStr=s2.replace(delStr,"");
}
}
}
if(newStr.equals(""))
return true;
else
return false;
}
回答1:
An easier way might be to just sort the chars in both strings, and compare whether they are equal:
public static boolean isAnagram(String s1, String s2){
// Early termination check, if strings are of unequal lengths,
// then they cannot be anagrams
if ( s1.length() != s2.length() ) {
return false;
}
s1=s1.toLowerCase();
s2=s2.toLowerCase();
char[] c1 = s1.toCharArray();
char[] c2 = s2.toCharArray();
Arrays.sort(c1);
Arrays.sort(c2);
String sc1 = new String(c1);
String sc2 = new String(c2);
return sc1.equals(sc2);
}
Personally I think it's more readable than nested for-loops =p
This has O(n log n) run-time complexity, where n
is the length of the longer string.
Edit: this is not the optimal solution. See @aam1r's answer for the most efficient approach (i.e. what you should actually say in an interview)
回答2:
This can be done in linear time using constant space. Here is pseudo-code to help you get started:
// Create new hashtable/hashmap to keep track of how many times each character
// is being used
character_map -> new hash map
// Initial check. If lengths are not the same, they can't be anagrams.
if s1.length != s2.length:
throw exception "Not anagrams"
// Add all characters from s1 to hashmap. Increment the value to keep track of
// number of occurences
foreach character c1 in s1:
character_map[c1]++
// Iterate through all character in s2 and decrement count of each character.
foreach character c2 in s2:
character_map[c2]--
// If they are anagrams, each character should be at "0" count at the point.
// If we come across a character that is not, it means that they are not anagrams
foreach key k, value v in character_map:
if v != 0:
throw exception "Not anagrams"
This code does not sort and hence can be done using simple loops. The overall runtime is O(n) and overall space is O(1) -- hence being the fastest solution. The number of elements you can have in the hash map is constant (i.e. you know how many items there are in your alphabet set).
回答3:
if(s1.charAt(i)==s2.charAt(j))
delStr=s1.substring(i,i+1);
newStr=s2.replace(delStr,"");
This code is a nice demonstration of why you should always have curly braces
around your if
, even if there is only a single statement. Your 2nd assignment is actually outside the if-condition
, and will always happen.
The best way to check for two strings to be Anagram
is to convert them to a character array (String#toCharArray)
. Then sort them using Arrays.sort
method. And do the comparison on them.
Updated : -
If you just want to use String
methods, then you don't actually need a nested loop there. You can do it with just one.
Here's the modified code of yours: -
public static boolean isAnagram(String s1 , String s2){
if (s1.length() != s2.length()) {
return false;
}
for(int i = 0; i < s2.length(); i++) {
if( !s1.contains("" + s2.charAt(i))) {
return false;
}
s1 = s1.replaceFirst("" + s2.charAt(i), "");
s2 = s2.replaceFirst("" + s2.charAt(i), "");
}
return true;
}
回答4:
I'm not sure what you're trying to do, but I'm pretty sure it won't work (and it runs in O(n^2)
. Try this (which runs in O(n log n)
) instead:
public static boolean isAnagram(String s1, String s2){
if (s1.length() != s2.length()) return false;
char[] c1 = s1.toCharArray();
char[] c2 = s2.toCharArray();
Arrays.sort(c1);
Arrays.sort(c2);
for(int i = 0; i < c1.length; i++) {
if(c1[i] != c2[i]) return false;
}
return true;
}
回答5:
What would be more efficient is to compare the Strings in sorted order.
public static boolean isAnagram(String s1 , String s2) {
return s1.length() == s2.length()
&& checkSum(s1) == checkSum(s2)
&& Arrays.equals(lettersSorted(s1), lettersSorted(s2));
}
static long checkSum(String s) {
long sqrSum = 0;
for(int i = 0; i < s.length(); s++) {
char ch = s.charAt(i);
sqrSum += ch + (1L << ch);
}
}
static char[] lettersSorted(String s) {
char[] chars = s.toCharArray();
Arrays.sort(chars);
return chars;
}
This is an O(N ln N) algorithm, but will be O(N) on an average if the Strings are typically not anagrams.
回答6:
There is a various possible solution of determining if a string is Anagram or not.
1. using Array.sort()
predefined method
String string1 = "abc";
String string2 = "bca";
char[] chars = string1.toCharArray();
char[] chars2 = string2.toCharArray();
Arrays.sort(chars);
Arrays.sort(chars2);
string1 = new String(chars);
string2 = new String(chars2);
if (string1.equalsIgnoreCase(string2)) {
System.out.println("Anagram");
} else {
System.out.println("Not Anagram");
}
Time complexity: Ω(n log n)
2. By Iterative method
char [] charArray = str.toCharArray();
if(str.length() == str1.length()){
for(char ch : charArray){
if(str1.indexOf(ch) == -1){
System.out.println("Not Anagram");
}
}
System.out.println("Anagram");
} else {
System.out.println("Not Anagram");
}
Time complexity: Ω(n)
Though, the first algorithm is more readable second algorithm really executes faster.
回答7:
The reason it doesn't work:
Using "des" and "sed" as an example.
In the last iteration for which it matches, it will evaluate:
if(s1.charAt(i)==s2.charAt(j))
{
delStr=s1.substring(i,i+1);
newStr=s2.replace(delStr,"");
}
Which will be: if( "s" == "s" )
It will then enter the if block, and evaluate
newStr = "sed".replace("s","");
which will give you "ed", instead of an empty string.
The moral of the story is that you are always replacing characters from s2 less one character, which will never be empty.
Using String.replace() is bad anyway, because it will replace all instances of the character by default. With String.replace(), it would consider "sed" to be an anagram of "seeeeeeed". You would do better to use String.replaceFirst().
In any case, a starting point is to make the following modifications:
String newStr = s2;
...
// inside if block
newStr = newStr.replaceFirst( delStr, "" );
回答8:
Below is a concise code snippet that determines whether two strings are anagrams in a single iteration of both strings, plus a final iteration of a 256 element array. This approach avoids sorting the characters in the strings and converting to/from Strings/char arrays by recording character counts in a mapping array.
static boolean isAnagram(String s1, String s2) {
if (s1.length() != s2.length()) return false;
int n = s1.length();
int[] charMap = new int[256];
for (int i = 0; i < n; i++) {
char c1 = s1.charAt(i);
charMap[c1]++;
char c2 = s2.charAt(i);
charMap[c2]--;
}
for (int i = 0; i < charMap.length; i++) {
if (charMap[i] != 0) return false;
}
return true;
}
This code basically increments and decrements an index location in an array corresponding to a character. If any of the array elements are non-zero at the end of the iteration, there were an unequal amount of increments and decrements, and therefore the strings contain differing characters and cannot be anagrams of each other.
Given that this algorithm iterates the two same sized strings once, runtime is O(n). Space complexity is O(1) as the charMap is always constant depending on charset requirements.
回答9:
import java.util.Scanner;
public class Anagrams {
static boolean isAnagram(String a, String b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a.length() != b.length()) {
return false;
}
char[] chars = a.toCharArray();
for (char c : chars) {
int index = b.indexOf(c);
if (index != -1) {
b = b.substring(0, index) + b.substring(index + 1, b.length());
} else {
return false;
}
}
return b.isEmpty();
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String a = scan.next();
String b = scan.next();
scan.close();
boolean ret = isAnagram(a, b);
System.out.println((ret) ? "Anagrams" : "Not Anagrams");
}
}
回答10:
public class SampleAnagram {
public static void main(String ...s) {
String s1 = "work";
String s2="kwro";
System.out.println(s1.charAt(0));
int s1L = s1.length();
int s2L = s2.length();
int totalCount = 0;
for(int i=0;i<s1L;i++) {
for(int j=0;j<s2L;j++) {
if(s1.charAt(i)==(s2.charAt(j))) {
totalCount = totalCount+1;
}
}
}
System.out.println(totalCount);
if(s1.length()==totalCount && s2.length()==totalCount) {
System.out.println("given the input as anagram");
}
}
}
回答11:
Here is another suggestion without initialising an int[256] but an int[26] for English alphabet.
public static void main(String[] args) {
System.out.println(isAnagram("restful", "fluster"));
}
static boolean isAnagram(String s1, String s2) {
if (s1.length() != s2.length()) {
return false;
}
int[] countArray = new int[26];
for (int i = 0; i < s1.length(); i++) {
countArray[getIndex(i, s1)]++;
countArray[getIndex(i, s2)]--;
}
for (int i = 0; i < countArray.length; i++) {
if (countArray[i] != 0) {
return false;
}
}
return true;
}
public static int getIndex(int position, String value) {
return value.charAt(position) - 'a';
}
Best George Tsopouridis
回答12:
public boolean checkAnagram(String s, String t) {
s = s.toLowerCase();
t = t.toLowerCase();
// We can ignore blanks
char[] word1 = s.replaceAll("\\s","").toCharArray();
char[] word2 = t.replaceAll("\\s","").toCharArray();
// Anagrams length should be the same
if (word1.length != word2.length) {
return false;
}
// Sorting arrays is pretty fast, it can be O(logn)
Arrays.sort(word1);
Arrays.sort(word2);
if (Arrays.equals(word1, word2)) {
return true;
}
return false;
}
回答13:
Using HashMap
public boolean isAnagram(String word, String anagram) {
if (word.length() != anagram.length())
return false;
int count = 0;
Map<Character, Integer> map = new HashMap<>();
for (int i = 0; i < word.length(); i++) {
if (!map.containsKey(word.charAt(i)))
map.put(word.charAt(i), 1);
else
map.put(word.charAt(i), map.get(word.charAt(i)) + 1);
}
for (int i = 0; i < anagram.length(); i++) {
if (!map.containsKey(anagram.charAt(i)))
return false;
else if (map.get(anagram.charAt(i)) >= 1)
map.put(anagram.charAt(i), map.get(anagram.charAt(i)) - 1);
else
return false;
}
return true;
}
回答14:
just making sure, you are trying to check to see if s1 is a anagram of s2 correct? That would also mean s2 is a an anagram of s1. So i would just sort s1 and s2 and check to see if they are equal.
String string1 = "fdafdas";
String string2 = "fdwqkjl";
char[] chars = string1.toCharArray();
char[] chars2 = string2.toCharArray();
Arrays.sort(chars);
Arrays.sort(chars2);
string1 = new String(chars);
string2 = new String(chars2);
if (string1.equals(string2)) {
//They are an anagram
}
回答15:
public boolean isAnagram(String a, String b) {
boolean result = false;
final String one = a.replaceAll("[\\s+\\W+]", "").toLowerCase();
final String two = b.replaceAll("[\\s+\\W+]", "").toLowerCase();
if (one.length() == two.length()) {
final char[] oneArray = one.toCharArray();
final char[] twoArray = two.toCharArray();
Arrays.sort(oneArray);
Arrays.sort(twoArray);
result = Arrays.equals(oneArray, twoArray);
}
return result;
}
回答16:
O(n) solution without any kind of sorting and using only one map. Also added proper null check missing in other solutions.
public boolean isAnagram(String leftString, String rightString) {
if (leftString == null || rightString == null) {
return false;
} else if (leftString.length() != rightString.length()) {
return false;
}
Map<Character, Integer> occurrencesMap = new HashMap<>();
for(int i = 0; i < leftString.length(); i++){
char charFromLeft = leftString.charAt(i);
int nrOfCharsInLeft = occurrencesMap.containsKey(charFromLeft) ? occurrencesMap.get(charFromLeft) : 0;
occurrencesMap.put(charFromLeft, ++nrOfCharsInLeft);
char charFromRight = rightString.charAt(i);
int nrOfCharsInRight = occurrencesMap.containsKey(charFromRight) ? occurrencesMap.get(charFromRight) : 0;
occurrencesMap.put(charFromRight, --nrOfCharsInRight);
}
for(int occurrencesNr : occurrencesMap.values()){
if(occurrencesNr != 0){
return false;
}
}
return true;
}
and less generic solution but a little bit faster one:
public boolean isAnagram(String leftString, String rightString) {
if (leftString == null || rightString == null) {
return false;
} else if (leftString.length() != rightString.length()) {
return false;
}
char letters[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
Map<Character, Integer> occurrencesMap = new HashMap<>();
for (char l : letters) {
occurrencesMap.put(l, 0);
}
for(int i = 0; i < leftString.length(); i++){
char charFromLeft = leftString.charAt(i);
Integer nrOfCharsInLeft = occurrencesMap.get(charFromLeft);
occurrencesMap.put(charFromLeft, ++nrOfCharsInLeft);
char charFromRight = rightString.charAt(i);
Integer nrOfCharsInRight = occurrencesMap.get(charFromRight);
occurrencesMap.put(charFromRight, --nrOfCharsInRight);
}
for(Integer occurrencesNr : occurrencesMap.values()){
if(occurrencesNr != 0){
return false;
}
}
return true;
}
回答17:
Here's a Simpler approach relying mostly on java The complete code is here https://github.com/rdsr/algorithms/blob/master/src/jvm/misc/AnagramsList.java (Note this solves a related by different problem)
class Anagram {
Map<Character, Integer> anagram;
Anagram(String s) {
anagram = new HashMap<Character, Integer>();
for (final Character c : s.toCharArray()) {
if (anagram.containsKey(c)) {
anagram.put(c, 1 + anagram.get(c));
} else {
anagram.put(c, 1);
}
}
}
@Override
public int hashCode() {
//.. elided
}
@Override
public boolean equals(Object obj) {
//.. elided
}
}
public class Anagrams {
public static void main(String[] args) {
System.out.println(new Anagram("abc").equals(new Anagram("bac")));
}
}
回答18:
Faster version using bit vector approach for anagram substrings
public boolean isAnagram(String _source1, String _source2)
{
int flag = 0, char_index = 0, counter = 0;
if(_source2.length() < _source1.length()){
return false;
}
char[] _stringchar = _source1.toCharArray();
char[] _tocheck = _source2.toCharArray();
for(char character : _stringchar)
{
char_index = character - 'a';
if((flag & (1 << char_index)) == 0)
flag |= (1 << char_index);
}
for(char toCheckcChar : _tocheck)
{
char_index = toCheckcChar - 'a';
if((flag & (1 << char_index)) > 0)
counter++;
else
counter = 0;
if(counter == _source1.length())
return true;
}
return false;
}
回答19:
The simple reason is, because replace function creates a new String
object. It does not do anything to the actual string (in your case s2
), because in Java, strings are final by nature. So as pointed out by cmonkey, you are always removing one character from your string s2, but in reality a new String
object is created with 1 character less, s2 remains as is.
The simple way to get this working in your case would be to create a new string object and assign it to yourself.
{
s2=s2.replace(delString,"");
....
if(s2.empty()) return true;
return false;
}
回答20:
once i made a test for job interview about anagrams. I uploaded it to my github account. The java code checks if a text file (with multiple lines) is an anagram poem or anagram text.
https://github.com/javocsoft/anagram_poem_checker
Hope it helps!
Bye.
回答21:
just see at line newStr=s2.replace(delStr,"");
what you are doing here replacing a char in s2 and assigning back to newStr, means you are not changing anything in s2. Just replace this code by below one it will work fine
newStr=newStr.replace(delStr,"");
回答22:
I took some time out to actually pen down the logic and write a code to check for two strings, if they are anagrams or not. Of course with the help of the above answers! XD
public static void main(String[] args) {
Map<Character, Integer> char_map = new HashMap<Character, Integer>();
Map<Character, Integer> empty_map = new HashMap<Character, Integer>();
String a = "HelloP";
String b = "HePlol";
if (a.length() != b.length()) {
System.out.println("false");
System.exit(0);
}
for (char c : a.toLowerCase().toCharArray()) {
empty_map.put(c, 0);
if (char_map.containsKey(c))
char_map.put(c, 1 + char_map.get(c));
else
char_map.put(c, 1);
}
for (char c : b.toLowerCase().toCharArray())
if (char_map.containsKey(c))
char_map.put(c, char_map.get(c) - 1);
System.out.println(char_map.equals(empty_map));
}
回答23:
I think this works with complexity O(2n)
public static boolean isAnagram(String str1, String str2){
if(str1.length() != str2.length()){ return false;}
int[] buffer = new int[256];
for(char ch : str1.toCharArray()){
buffer[ch]++;
}
for(char ch : str2.toCharArray()){
if(buffer[ch]==0) return false;
buffer[ch] = (buffer[ch] > 0)?(buffer[ch] - 1 ): -1 ;
}
return true;
}
回答24:
This is a Java implementation that I wrote for using an array instead of a HashMap. This saves space, plus arrays are really fast.
public static boolean anagram(String s, String t) {
if (s.length() != t.length()) return false;
int[] arr = new int[123];
for (char c : s.toCharArray())
arr[c]++;
for (char c : t.toCharArray())
arr[c]--;
for (int i : arr)
if (i != 0)
return false;
return true;
}
回答25:
import java.util.Scanner;
public class JavaProgram
{
public static void main(String[] input)
{
String str1, str2;
int len, len1, len2, i, j, found=0, not_found=0;
Scanner scan = new Scanner(System.in);
System.out.print("Enter First String : ");
str1 = scan.nextLine();
System.out.print("Enter Second String : ");
str2 = scan.nextLine();
len1 = str1.length();
len2 = str2.length();
if(len1 == len2)
{
len = len1;
for(i=0; i<len; i++)
{
found = 0;
for(j=0; j<len; j++)
{
if(str1.charAt(i) == str2.charAt(j))
{
found = 1;
break;
}
}
if(found == 0)
{
not_found = 1;
break;
}
}
if(not_found == 1)
{
System.out.print("Strings are not Anagram to Each Other..!!");
}
else
{
System.out.print("Strings are Anagram");
}
}
else
{
System.out.print("Both Strings Must have the same number of Character to be an Anagram");
}
}
}
回答26:
public class Anagram {
public boolean isAnagram(
String left,
String right) {
if (left.length() == right.length()) {
Map<Character, Integer> map = new HashMap<>();
char[] a = left.toCharArray(), b = right.toCharArray();
for (int i = 0; i < a.length; i++) {
accumulate(map, a[i]);
accumulate(map, b[i]);
}
for (char c : map.keySet()) {
if (map.get(c) > 0) {
return false;
}
}
return true;
} else {
return false;
}
}
private void accumulate(
Map<Character, Integer> map,
char key) {
if (map.containsKey(key)) {
map.put(key, Math.abs(map.get(key) - 1));
} else {
map.put(key, 1);
}
}
}
回答27:
Here is my solution from your aspect
private static boolean isAnagram(String s1, String s2){
int count = 0;
boolean flag = false;
if(s1.length() != s2.length()){
return false;
}
//checks whether both word's letters are the same
for (int i = 0; i < s1.length(); i++){
for (int j = 0; j < s2.length(); j++){
if(s1.charAt(i) == s2.charAt(j)){
count++;
break;
}
}
}
//if count equals to one of the Strings length then it is an anagram
if(count == s2.length() ){
flag = true;
}
return flag;
}
回答28:
String str1="Mother In Law";
String str2="Hitler Woman";
char[] anag1=str1.replaceAll("\\s", "").toLowerCase().toCharArray();
char[] anag2=str2.replaceAll("\\s", "").toLowerCase().toCharArray();
Arrays.sort(anag1);
Arrays.sort(anag2);
System.out.println(Arrays.equals(anag1, anag2)? "words are anagrams":"words are not anagrams");
回答29:
I guess the following solution has O(n)
complexity, let me know if someone differs.
import java.util.HashMap;
import java.util.Scanner;
public class Anagrams {
static boolean isAnagram(String word1, String word2)
{
if(word1.length() != word2.length()) {
return false;
}
int flag=0;
HashMap<Character,Integer> table = new HashMap<Character,Integer>();
for(int i=0; i< word1.length();i++) {
table.put(word1.charAt(i),1);
}
for(int i=0; i< word2.length();i++) {
if(table.containsKey(word2.charAt(i))) {
continue;
} else {
flag=1;
break;
}
}
return flag == 0;
}
public static void main(String[] args) {
System.out.println("Enter your string");
Scanner sc= new Scanner(System.in);
String word1= sc.nextLine();
String word2=sc.nextLine();
boolean result = isAnagram(word1,word2);
if(result) {
System.out.println("The words are Anagrams");
} else{
System.out.println("The words are not Anagrams");
}
}
}
回答30:
public static boolean isAnagram(String s1, String s2) {
boolean aux = true;
if (s1.length() != s2.length()){
aux = false;
}else{
for (int i = 0; i < s1.length(); i++)
if(s2.toUpperCase().indexOf(s1.toUpperCase().substring(i, i+1)) == -1) aux = false;
}
return aux;
}
来源:https://stackoverflow.com/questions/13692221/anagram-algorithm-in-java