开发者

Checking if two strings are permutations of each other

开发者 https://www.devze.com 2022-12-17 20:40 出处:网络
How to determine if two strings are permu开发者_如何学JAVAtations of each other Sort the two strings\'s characters.

How to determine if two strings are permu开发者_如何学JAVAtations of each other


  • Sort the two strings's characters.
  • Compare the results to see if they're identical.

Edit:

The above method is reasonably efficient - O(n*log(n)) and, as others have shown, very easy to implement using the standard Java API. Even more efficient (but also more work) would be counting and comparing the occurrence of each character, using the char value as index into an array of counts.

I do not thing there is an efficient way to do it recursively. An inefficient way (O(n^2), worse if implemented straightforwardly) is this:

  • If both strings consist of one identical character, return true
  • Otherwise:
    • remove one character from the first string
    • Look through second string for occurrence of this character
    • If not present, return false
    • Otherwise, remove said character and apply algorithm recursively to the remainders of both strings.


To put @Michael Borgwardt's words in to code:

public boolean checkAnagram(String str1, String str2) {

    if (str1.length() != str2.length())
      return false;

    char[] a = str1.toCharArray();
    char[] b = str2.toCharArray();

    Arrays.sort(a);
    Arrays.sort(b);

    return Arrays.equals(a, b);
}


Create a Hashmap with the characters of the first string as keys and the number of occurances as value; then go through the second string and for each character, look up the hash table and decrement the number if it is greater than zero. If you don't find an entry or if it is already 0, the strings are not a permutation of each other. Obviously, the string must have the same length.


Linear Time solution in HashMap. Traverse and put first String in HashMap, keep the count of each character. Traverse second String and if it is already in the hashmap decrease the count by 1. At the end if all character were in the string the value in hashmap will be 0 for each character.

public class StringPermutationofEachOther {
    public static void main(String[] args)
    {

    String s1= "abc";
    String s2 ="bbb";
    System.out.println(perm(s1,s2));
}
    public static boolean perm(String s1, String s2)
    { HashMap<Character, Integer> map = new HashMap<Character, Integer>();
    int count =1;
        if(s1.length()!=s2.length())
        {
            return false;
        }
            for(Character c: s1.toCharArray())
            {
                if(!map.containsKey(c))
                    map.put(c, count);

                else
                    map.put(c, count+1);

            }
            for(Character c: s2.toCharArray())
            {
                if(!map.containsKey(c))
                    return false;

                else
                    map.put(c, count-1);
            }
            for(Character c: map.keySet())
            {
                if(map.get(c)!=0)
                    return false;
            }
        return true;
    }


}


You can try to use XOR, if one string is a permeation of the other, they should have essentially identical chars. The only difference is just the order of chars. Therefore using XOR trick can help you get rid of the order and focus only on the chars.

public static boolean isPermutation(String s1, String s2){
    if (s1.length() != s2.length()) return false;
    int checker = 0;
    for(int i = 0; i < s1.length();i++ ){
        checker ^= s1.charAt(i) ^ s2.charAt(i);
    }

    return checker == 0;
}


  1. Sort the 2 strings by characters and compare if they're the same (O(n log n) time, O(n) space), or
  2. Tally the character frequency of the 2 strings and compare if they're the same (O(n) time, O(n) space).


You might take a look at String.toCharArray and Arrays.sort


First you check the lengths (n), if they are not same, they cannot be permutations of each other. Now create two HashMap<Character, Integer>. Iterate over each string and put the number of times each character occur in the string. E.g. if the string is aaaaa, the map will have just one element with key a and value 5. Now check if the two maps are identical. This is an O(n) algorithm.

EDIT with code snippet :

boolean checkPermutation(String str1, String str2) {

char[] chars1 = str1.toCharArray();
char[] chars2 = str2.toCharArray();

Map<Character, Integer> map1 = new HashMap<Character, Integer>();
Map<Character, Integer> map2 = new HashMap<Character, Integer>();

for (char c : chars1) {
   int occ = 1;
   if (map1.containsKey(c) {
       occ = map1.get(c);
       occ++;
   }
   map1.put(c, occ);
}

// now do the same for chars2 and map2

if (map1.size() != map2.size()) {
   return false;
}
for (char c : map1.keySet()) {

    if (!map2.containsKey(c) || map1.get(c) != map2.get(c)) {
        return false;
    }
}

return true;
}


I'm working on a Java library that should simplify your task. You can re-implement this algorithm using only two method calls:

boolean arePermutationsOfSameString(String s1, String s2) {
    s1 = $(s1).sort().join(); 
    s2 = $(s2).sort().join();
    return s1.equals(s2);
}

testcase

@Test
public void stringPermutationCheck() {
    // true cases
    assertThat(arePermutationsOfSameString("abc", "acb"), is(true));
    assertThat(arePermutationsOfSameString("bac", "bca"), is(true));
    assertThat(arePermutationsOfSameString("cab", "cba"), is(true));

    // false cases
    assertThat(arePermutationsOfSameString("cab", "acba"), is(false));
    assertThat(arePermutationsOfSameString("cab", "acbb"), is(false));

    // corner cases
    assertThat(arePermutationsOfSameString("", ""), is(true));
    assertThat(arePermutationsOfSameString("", null), is(true));
    assertThat(arePermutationsOfSameString(null, ""), is(true));
    assertThat(arePermutationsOfSameString(null, null), is(true));
}

PS

In the case you can clone the souces at bitbucket.


I did this, and it works well and quickly:

public static boolean isPermutation(String str1, String str2)
{
    char[] x = str1.toCharArray();
    char[] y = str2.toCharArray();
    Arrays.sort(x);
    Arrays.sort(y);
    if(Arrays.equals(x, y))
        return true;
    return false;
}


public boolean isPermutationOfOther(String str, String other){
    if(str == null || other == null)
        return false;
    if(str.length() != other.length())
        return false;

    Map<Character, Integer> characterCount = new HashMap<Character, Integer>();
    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
        int count = 1;
        if(characterCount.containsKey(c)){
            int k = characterCount.get(c);
            count = count+k;
        }

        characterCount.put(c, count);

    }

    for (int i = 0; i < other.length(); i++) {
        char c = other.charAt(i);
        if(!characterCount.containsKey(c)){
            return false;
        }

        int count = characterCount.get(c);
        if(count == 1){
            characterCount.remove(c);
        }else{
            characterCount.put(c, --count);
        }
    }

    return characterCount.isEmpty();
}


Variation on other approaches but this one uses 2 int arrays to track the chars, no sorting, and you only need to do 1 for loop over the strings. The for loop I do over the int arrays to test the permutation is a constant, hence not part of N.

Memory is constant.

O(N) run time.

// run time N, no sorting, assume 256 ASCII char set
public static boolean isPermutation(String v1, String v2) {

    int length1 = v1.length();
    int length2 = v2.length();
    if (length1 != length2)
        return false;

    int s1[] = new int[256];
    int s2[] = new int[256];

    for (int i = 0; i < length1; ++i) {
        int charValue1 = v1.charAt(i);
        int charValue2 = v2.charAt(i);
        ++s1[charValue1];
        ++s2[charValue2];
    }

    for (int i = 0; i < s1.length; ++i) {

        if (s1[i] != s2[i])
            return false;
    }
    return true;
  }
}

Unit Tests

@Test
public void testIsPermutation_Not() {
    assertFalse(Question3.isPermutation("abc", "bbb"));
}

@Test
public void testIsPermutation_Yes() {
    assertTrue(Question3.isPermutation("abc", "cba"));
    assertTrue(Question3.isPermutation("abcabcabcabc", "cbacbacbacba"));
}


If we do the initial length check, to determine if the two strings are of the same length or not,

    public boolean checkIfPermutation(String str1, String str2) {
      if(str1.length()!=str2.length()){
        return false;
      }
      int str1_sum = getSumOfChars(str1);
      int str2_sum = getSumOfChars(str2);
      if (str1_sum == str2_sum) {
        return true;
      }
      return false;
}

public int getSumOfChars(String str){
      int sum = 0; 
      for (int i = 0; i < str.length(); i++) {
        sum += str.charAt(i);
      }
      return sum;
}


The obligatory Guava one-liner:

boolean isAnagram(String s1, String s2) {
    return ImmutableMultiset.copyOf(Chars.asList(s1.toCharArray())).equals(ImmutableMultiset.copyOf(Chars.asList(s2.toCharArray())));
}

(Just for fun. I don't recommend submitting this for your assignment.)


As you requested, here's a complete solution using recursion. Now all you have to do is:

  1. Figure out what language this is
  2. Translate it to Java.

Good luck :-)

proc isAnagram(s1, s2);
  return {s1, s2} = {''} or
         (s2 /= '' and
          (exists c = s1(i) |
                  s2(1) = c and
                  isAnagram(s1(1..i-1) + s1(i+1..), s2(2..))));
end isAnagram;


I did it using C#

bool Arepermutations(string string1, string string2)
        {
            char[] str1 = string1.ToCharArray();
            char[] str2 = string2.ToCharArray();
            if (str1.Length !=str2.Length)
              return false; 
            Array.Sort(str1); 
            Array.Sort(str2);
            if (str1.Where((t, i) => t!= str2[i]).Any())
            {
                return false;
            }

            return true; 

        }


public boolean permitation(String s,String t){
      if(s.length() != t.length()){
          return false;
          }

       int[] letters = new int[256];//Assumes that the character set is ASCII
       char[] s_array = s.toCharArray();

       for(char c:s_array){         /////count number of each char in s
             letters[c]++;
        }
       for(int i=0;i<t.length();i++){
             int c = (int)t.charAt(i);
             if(--letters[c]<0){
                  return false;
             }
        }
        return true;
}


             import java.io.*;
                      public class permute 
                    {
                              public static String sort(String s)
                              {
                                     char[] str = s.toCharArray();
                                    java.util.Arrays.sort(str);
                                    return new String(str);
                              }
                      public static boolean permutation(String s,String t)
                     {
                            if(s.length()!=t.length())
                           {
                                   return false;
                           }
                                   return sort(s).equals(sort(t));
                     }
    public static void main(String[] args)            throws IOException
    {
           BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
           String string = null;
           boolean x=true;
           System.out.println("Input String:");
           string = bf.readLine();
           System.out.println("Input another String:");
           String result = bf.readLine();
           String resultant = sort(string);
           if(x==permutation(result,resultant))
           {
                    System.out.println("String"+" "+"("+result+")"+"is a permutation of String"+" "+"("+string+")");
           }
           else
           {
                    System.out.println("Sorry No anagram found");
           }       
    }

}


public class TwoStrgPermutation {

public int checkForUnique(String s1, String s2)
{
    int[] array1 = new int[256];
    int[] array2 = new int[256];

    array1 = arrayStringCounter(array1,s1);
    array2 = arrayStringCounter(array2,s2);

    if(Arrays.equals(array1, array2))
        return 0;
    else
        return 1;
}

public int[] arrayStringCounter(int[] array,String s)
{
    int val;
    for(int i=0;i<s.length();i++)
    {
        val = (int)s.charAt(i);
        array[val]++;
    }

    return array;
}

public static void main(String[] args) {
    // TODO Auto-generated method stub

    InputStreamReader in = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(in);
    TwoStrgPermutation obj = new TwoStrgPermutation();
    try {
        String string1 = br.readLine();
        String string2 = br.readLine();

        int len1 = string1.length();
        int len2 = string2.length();

        if(len1==len2)
        {
            int result = obj.checkForUnique(string1,string2);
            if (result == 0){
                System.out.println("yes it is a permutation");
            }
            else if (result >0)
            {
                System.out.println("no it is not");
            }
        }
        else
        {
            System.out.println("no it is not");
        }

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

}


>>>def isPermutation = lambda x, y: set([i for i in x]) == set([j for j in x])
>>>isPermutation("rasp", "spar")
>>>True


This is an O(N) solution, in which N is the length of the shorter string. It has a disadvantage, which is that only ASCII characters are acceptable. If we want better applicability, we may substitute a hash table for the int charNums[]. But it also means C wouldn't be a good choice, coz' there is no standard hash table implementation for C.

int is_permutation(char *s1, char *s2)
{
    if ((NULL == s1) ||
        (NULL == s2)) {
        return false;
    }

    int static const
    _capacity = 256;    // Assumption
    int
    charNums[_capacity] = {0};
    char
    *cRef1 = s1,
    *cRef2 = s2;
    while ('\0' != *cRef1 && '\0' != *cRef2) {
        charNums[*cRef1] += 1;
        charNums[*cRef2] -= 1;
        cRef1++;
        cRef2++;
    }

    if ('\0' != *cRef1 || '\0'  != *cRef2) {
        return false;
    }

    for (int i = 0; i < _capacity; i++) {
        if (0 != charNums[i]) {
            return false;
        }
    }

    return true;
}


Create two methods:

1. First method takes a string and returns a sorted string:

public String sort(String str) { char char_set[] = str.toCharArray(); Arrays.sort(char_set); return new String(char_set); }

2. Second method takes two strings and return a boolean:

   `public boolean sort(String x, String y) {
       if (x.length() != y.length()) {
           System.out.println("false");
           return false;
       }
       System.out.println(sort(x).equals(sort(y)));
       return sort(x).equals(sort(y));
   }`


It can be done by using a Dictionary in C#. A basic implementation is like :

    private static bool IsPermutation(string str1, string str2)
    {
        if (str1.Length != str2.Length)
            return false;

        var dictionary = new Dictionary<char, int>();

        foreach(var x in str1)
        {
            if (dictionary.ContainsKey(x))
                dictionary[x] += 1;
            else
                dictionary.Add(x, 1);
        }

        foreach (var y in str2)
        {
            if (dictionary.ContainsKey(y))
            {
                if (dictionary[y] > 0)
                    dictionary[y] -= 1;
                else
                    return false;
            }
            else
                return false;
        }

        foreach(var el in dictionary)
        {
            if (el.Value != 0)
                return false;
        }

        return true;
    }

Time Complexity is O(n), linear solution.


    public static boolean isPermutation(String s1, String s2) {
    if (s1.length() != s2.length()) {
        return false;
    }else if(s1.length()==0 ){
        return true;
    }
    else if(s1.length()==1){
        return s1.equals(s2);
    }
    char[] s = s1.toCharArray();
    char[] t = s2.toCharArray();

    for (int i = 0; i < s.length; i++) {
        for (int j = 0; j < t.length; j++) {

            if (s.length == s1.length() && (i == 0 && j == t.length - 1) && s[i] != t[j]) {
                return false;
            }
            if (s[i] == t[j]) {
                String ss = new String(s);
                String tt = new String(t);
                s = (ss.substring(0, i) + ss.substring(i + 1, s.length)).toCharArray();
                t = (tt.substring(0, j) + tt.substring(j + 1, t.length)).toCharArray();

                System.out.println(new String(s));
                System.out.println(new String(t));

                i = 0;
                j = 0;
            }
        }
    }
    return s[0]==t[0] ;
}

This solution works for any charset. With an O(n) complexity.

The output for: isPermutation("The Big Bang Theory", "B B T Tehgiangyroeh")

he Big Bang Theory
B B  Tehgiangyroeh
e Big Bang Theory
B B  Tegiangyroeh
 Big Bang Theory
B B  Tgiangyroeh
Big Bang Theory
BB  Tgiangyroeh
ig Bang Theory
B  Tgiangyroeh
g Bang Theory
B  Tgangyroeh
 Bang Theory
B  Tangyroeh
Bang Theory
B Tangyroeh
Bng Theory
B Tngyroeh
Bg Theory
B Tgyroeh
B Theory
B Tyroeh
BTheory
BTyroeh
Bheory
Byroeh
Beory
Byroe
Bory
Byro
Bry
Byr
By
By
B
B
true


Best Way to do this is by sorting the two strings first and then compare them. It's not the most efficient way but it's clean and is bound to the runtime of the sorting routine been used.

boolean arePermutation(String s1, String s2) { 
  if(s1.lenght() != s2.lenght()) {
     return false;
   }
   return mySort(s1).equals(mySort(s2));
 }

  String mySort(String s) {
   Char letters[] = s.toCharArray();
   Arrays.sort(letters);
   return new String(letters);
}


    String str= "abcd";
    String str1 ="dcazb";
    int count=0;

    char s[]= str.toCharArray();
    char s1[]= str1.toCharArray();

    for(char c:s)
    {
        count = count+(int)c ;
    }

    for(char c1:s1)
    {
        count=count-(int)c1;

    }

    if(count==0)
    System.out.println("String are Anagram");
    else
    System.out.println("String are not Anagram");

}


Here is a simple program I wrote that gives the answer in O(n) for time complexity and O(1) for space complexity. It works by mapping every character to a prime number and then multiplying together all of the characters in the string's prime mappings together. If the two strings are permutations then they should have the same unique characters each with the same number of occurrences.

Here is some sample code that accomplishes this:

// maps keys to a corresponding unique prime
static Map<Integer, Integer> primes = generatePrimes(255); // use 255 for
                                                            // ASCII or the
                                                            // number of
                                                            // possible
                                                            // characters

public static boolean permutations(String s1, String s2) {
    // both strings must be same length
    if (s1.length() != s2.length())
        return false;

    // the corresponding primes for every char in both strings are multiplied together
    int s1Product = 1;
    int s2Product = 1;

    for (char c : s1.toCharArray())
        s1Product *= primes.get((int) c);

    for (char c : s2.toCharArray())
        s2Product *= primes.get((int) c);

    return s1Product == s2Product;

}

private static Map<Integer, Integer> generatePrimes(int n) {

    Map<Integer, Integer> primes = new HashMap<Integer, Integer>();

    primes.put(0, 2);

    for (int i = 2; primes.size() < n; i++) {
        boolean divisible = false;

        for (int v : primes.values()) {
            if (i % v == 0) {
                divisible = true;
                break;
            }
        }

        if (!divisible) {
            primes.put(primes.size(), i);
            System.out.println(i + " ");
        }
    }

    return primes;

}


Based on the comment on this solution, https://stackoverflow.com/a/31709645/697935 here's that approach, revised.

private static boolean is_permutation(String s1, String s2) {
    HashMap<Character, Integer> map = new HashMap<Character, Integer>();
    int count = 1;
    if(s1.length()!=s2.length()) {
        return false;
    }
    for(Character c: s1.toCharArray()) {
        if(!map.containsKey(c)) {
            map.put(c, 1);
        }
        else {
            map.put(c, map.get(c) + 1);
        }
    }
    for(Character c: s2.toCharArray()) {
        if(!map.containsKey(c)) {
            return false;
        }
        else {
            map.put(c, map.get(c) - 1);
        }
    }
    for(Character c: map.keySet()) {
        if(map.get(c) != 0) { return false; }
    }
    return true;
}


bool is_permutation1(string str1, string str2) {
    sort(str1.begin(), str1.end());
    sort(str2.begin(), str2.end());
    for (int i = 0; i < str1.length(); i++) {    
        if (str1[i] != str2[i]) {
            return false;
        }
    }
    return true;
}


I wanted to give a recursive solution for this problem as I did not find any answer which was recursive. I think that this code seems to be tough but if you'll try to understand it you'll see the beauty of this code. Recursion is sometimes hard to understand but good to use! This method returns all the permutations of the entered String 's' and keeps storing them in the array 'arr[]'. The value of 't' initially is blank "" .

import java.io.*;
class permute_compare2str
{
    static String[] arr= new String [1200];
    static int p=0;
    void permutation(String s,String t)
    {
        if (s.length()==0)
        {
            arr[p++]=t;
            return;
        }
        for(int i=0;i<s.length();i++)
            permutation(s.substring(0,i)+s.substring(i+1),t+s.charAt(i));
    }
    public static void main(String kr[])throws IOException
    {
        int flag = 0;
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("Enter the first String:");
        String str1 = br.readLine();
        System.out.println("Enter the second String:");
        String str2 = br.readLine();
        new permute_compare2str().permutation(str1,"");
        for(int i = 0; i < p; ++i)
        {
            if(arr[i].equals(str2))
            {
                flag = 1;
                break;
            }
        }
        if(flag == 1)
            System.out.println("True");
        else
        {
            System.out.println("False");
            return;
        }
    }
}

One limitation that I can see is that the length of the array is fixed and so will not be able to return values for a large String value 's'. Please alter the same as per the requirements. There are other solutions to this problem as well.

I have shared this code because you can actually use this to get the permutations of a string printed directly without the array as well.

HERE:

void permutations(String s, String t)
    {
        if (s.length()==0)
        {
            System.out.print(t+" ");
            return;
        }
        for(int i=0;i<s.length();i++)
            permutations(s.substring(0,i)+s.substring(i+1),t+s.charAt(i));
    }

Value of 's' is the string whose permutations is needed and value of 't' is again empty "".

Reference: Introduction to Programming in Java

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号