ArrayList vs HashMap time complexity - java

The scenario is the following:
You have 2 strings (s1, s2) and want to check whether one is a permutation of the other so you generate all permutations of lets say s1 and store them and then iterate over and compare against s2 until either it's found or not.
Now, in this scenario, i am deliberating whether an ArrayList is better to use or a HashMap when considering strictly time complexity as i believe both have O(N) space complexity.
According to the javadocs, ArrayList has a search complexity of O(N) whereas HashMap is O(1). If this is the case, is there any reason to favor using ArrayList over HashMap here since HashMap would be faster?
The only potential downside i could think of is that your (k,v) pairs might be a bit weird if you did something like where the key = value, i.e. {k = "ABCD", v = "ABCD"}, etc..

As shown here:
import java.io.*;
import java.util.*;
class GFG{
static int NO_OF_CHARS = 256;
/* function to check whether two strings
are Permutation of each other */
static boolean arePermutation(char str1[], char str2[])
{
// Create 2 count arrays and initialize
// all values as 0
int count1[] = new int [NO_OF_CHARS];
Arrays.fill(count1, 0);
int count2[] = new int [NO_OF_CHARS];
Arrays.fill(count2, 0);
int i;
// For each character in input strings,
// increment count in the corresponding
// count array
for (i = 0; i <str1.length && i < str2.length ;
i++)
{
count1[str1[i]]++;
count2[str2[i]]++;
}
// If both strings are of different length.
// Removing this condition will make the program
// fail for strings like "aaca" and "aca"
if (str1.length != str2.length)
return false;
// Compare count arrays
for (i = 0; i < NO_OF_CHARS; i++)
if (count1[i] != count2[i])
return false;
return true;
}
/* Driver program to test to print printDups*/
public static void main(String args[])
{
char str1[] = ("geeksforgeeks").toCharArray();
char str2[] = ("forgeeksgeeks").toCharArray();
if ( arePermutation(str1, str2) )
System.out.println("Yes");
else
System.out.println("No");
}
}
// This code is contributed by Nikita Tiwari.
If you're glued to your implementation, use a HashSet, it still has O(1) lookup time, just without keys

You can use HashSet as you need only one parameter.

Related

How to handle the time complexity for permutation of strings during anagrams search?

I have a program that computes that whether two strings are anagrams or not.
It works fine for inputs of strings below length of 10.
When I input two strings whose lengths are equal and have lengths of more than 10 program runs and doesn't produce an answer .
My concept is that if two strings are anagrams one string must be a permutation of other string.
This program generates the all permutations from one string, and after that it checks is there any matching permutation for the other string. In this case I wanted to ignore cases.
It returns false when there is no matching string found or the comparing strings are not equal in length, otherwise returns true.
public class Anagrams {
static ArrayList<String> str = new ArrayList<>();
static boolean isAnagram(String a, String b) {
// there is no need for checking these two
// strings because their length doesn't match
if (a.length() != b.length())
return false;
Anagrams.permute(a, 0, a.length() - 1);
for (String string : Anagrams.str)
if (string.equalsIgnoreCase(b))
// returns true if there is a matching string
// for b in the permuted string list of a
return true;
// returns false if there is no matching string
// for b in the permuted string list of a
return false;
}
private static void permute(String str, int l, int r) {
if (l == r)
// adds the permuted strings to the ArrayList
Anagrams.str.add(str);
else {
for (int i = l; i <= r; i++) {
str = Anagrams.swap(str, l, i);
Anagrams.permute(str, l + 1, r);
str = Anagrams.swap(str, l, i);
}
}
}
public static String swap(String a, int i, int j) {
char temp;
char[] charArray = a.toCharArray();
temp = charArray[i];
charArray[i] = charArray[j];
charArray[j] = temp;
return String.valueOf(charArray);
}
}
1. I want to know why can't this program process larger strings
2. I want to know how to fix this problem
Can you figure it out?
To solve this problem and check whether two strings are anagrams you don't actually need to generate every single permutation of the source string and then match it against the second one. What you can do instead, is count the frequency of each character in the first string, and then verify whether the same frequency applies for the second string.
The solution above requires one pass for each string, hence Θ(n) time complexity. In addition, you need auxiliary storage for counting characters which is Θ(1) space complexity. These are asymptotically tight bounds.
you're doing it in very expensive way and the time complexity here is exponential because your'e using permutations which requires factorials and factorials grow very fast , as you're doing permutations it will take time to get the output when the input is greater than 10.
11 factorial = 39916800
12 factorial = 479001600
13 factorial = 6227020800
and so on...
So don't think you're not getting an output for big numbers you will eventually get it
If you go something like 20-30 factorial i think i will take years to produce any output , if you use loops , with recursion you will overflow the stack.
fact : 50 factorial is a number that big it is more than the number of sand grains on earth , and computer surrender when they have to deal with numbers that big.
That is why they make you include special character in passwords to make the number of permutations too big that computers will not able to crack it for years if they try every permutations , and encryption also depends on that weakness of the computers.
So you don't have to and should not do that to solve it (because computer are not good very at it), it is an overkill
why don't you take each character from one string and match it with every character of other string, it will be quadratic at in worst case.
And if you sort both the strings then you can just say
string1.equals(string2)
true means anagram
false means not anagram
and it will take linear time,except the time taken in sorting.
You can first get arrays of characters from these strings, then sort them, and then compare the two sorted arrays. This method works with both regular characters and surrogate pairs.
public static void main(String[] args) {
System.out.println(isAnagram("ABCD", "DCBA")); // true
System.out.println(isAnagram("𝗔𝗕𝗖𝗗", "𝗗𝗖𝗕𝗔")); // true
}
static boolean isAnagram(String a, String b) {
// invalid incoming data
if (a == null || b == null
|| a.length() != b.length())
return false;
char[] aArr = a.toCharArray();
char[] bArr = b.toCharArray();
Arrays.sort(aArr);
Arrays.sort(bArr);
return Arrays.equals(aArr, bArr);
}
See also: Check if one array is a subset of the other array - special case

Dynamic Programming approach - Interleaving Parentheses

Below is my code for the problem described on https://community.topcoder.com/stat?c=problem_statement&pm=14635. It keeps track of possible interleaves (as described in the problem description given) through a static variable countPossible.
public class InterleavingParentheses{
public static int countPossible = 0;
public static Set<String> dpyes = new HashSet<>(); //used for dp
public static Set<String> dpno = new HashSet<>(); //used for dp
public static void numInterleaves(char[] s1, char[] s2, int size1, int size2){
char[] result = new char[size1+size2];
numInterleavesHelper(result,s1,s2,size1,size2,0,0,0);
}
public static void numInterleavesHelper(char[] res, char[] s1, char[] s2, int size1, int size2, int pos, int start1, int start2){
if (pos == size1+size2){
if (dpyes.contains(new String(res))){
countPossible+=1;
}
else{
if(dpno.contains(new String(res))){
countPossible+=0;
}
else if (isValid(res)){
dpyes.add(new String(res));
countPossible+=1;
}
else{
dpno.add(new String(res));
}
}
}
if (start1 < size1){
res[pos] = s1[start1];
numInterleavesHelper(res,s1,s2,size1,size2,pos+1,start1+1,start2);
}
if (start2 < size2){
res[pos] = s2[start2];
numInterleavesHelper(res,s1,s2,size1,size2,pos+1,start1,start2+1);
}
}
private static boolean isValid(char[] string){
//basically checking to see if parens are balanced
LinkedList<Character> myStack = new LinkedList<>();
for (int i=0; i<string.length; i++){
if (string[i] == "(".charAt(0)){
myStack.push(string[i]);
}
else{
if (myStack.isEmpty()){
return false;
}
if (string[i] == ")".charAt(0)){
myStack.pop();
}
}
}
return myStack.isEmpty();
}
}
I use the scanner class to put in the input strings s1 = "()()()()()()()()()()()()()()()()()()()()" and s2 = "()()()()()()()()()()()()()()()()()" into this function and while the use of the HashSet greatly lowers the time because duplicate interleaves are accounted for, large input strings still take up a lot of time. The sizes of the input strings are supposed to be at most 2500 characters and my code is not working for strings that long. How can i modify this to make it better?
Your dp set is only used at the end, so at best you can save an O(n), but you've already done many O(n) operations to reach that point so the algorithm completexity is about the same. For dp to be effective, you need to be reducing O(2^n) operations to, say O(n^2).
As one of the testcases has an answer of 487,340,184, then for your program to produce this answer, it would need that number of calls to numInterleavesHelper because each call can only increment countPossible by 1. The question asking for the answer "modulo 10^9 + 7" as well indicates that there is a large number expected as an answer.
This rules out things like creating every possible resulting string, most string manipulation, and counting 1 string at a time. Even if you optimized it, then the number of iterations alone makes it unfeasible.
Instead, think of algorithms that have about 10,000,000 iterations. Each string has a length of 2500. These constraints were chosen on purpose so that 2500 * 2500 fits within this number of iterations, suggesting a 2D dp solution.
If you create an array:
int ways[2501][2501] = new int[2501][2501];
then you want the answer to be:
ways[2500][2500]
Here ways[x][y] is the number of ways of creating valid strings where x characters have been taken from the first string, and y characters have been taken from the second string. Each time you add a character, you have 2 choices, taking from the first string or taking from the second. The new number of ways is the sum of the previous ones, so:
ways[x][y] = ways[x-1][y] + ways[x][y-1]
You also need to check that each string is valid. They're valid if each time you add a character, the number of opening parens minus the number of closing parens is 0 or greater, and this number is 0 at the end. The number of parens of each type in every prefix of s1 and s2 can be precalculated to make this a constant-time check.

First non-repeating character in a stream

My answer to this question is as follows, but I want to know if I can use this code and what will be the complexity:
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class FirstNonRepeatingCharacterinAString {
private char firstNonRepeatingCharacter(String str) {
LinkedHashMap<Character, Integer> hash =
new LinkedHashMap<Character, Integer>();
for(int i = 0 ; i< str.length() ; i++)
{
if(hash.get(str.charAt(i))==null)
hash.put(str.charAt(i), 1);
else
hash.put(str.charAt(i), hash.get(str.charAt(i))+1);
}
System.out.println(hash.toString());
for(Entry<Character, Integer> c : hash.entrySet())
{
if(c.getValue() == 1)
return c.getKey();
}
return 0 ;
}
public static void main(String args[])
{
String str = "geeksforgeeks";
FirstNonRepeatingCharacterinAString obj =
new FirstNonRepeatingCharacterinAString();
char c = obj.firstNonRepeatingCharacter(str);
System.out.println(c);
}
}
Your question about whether you "can use this code" is a little ambiguous - if you wrote it, I'd think you can use it :)
As for the complexity, it is O(n) where n is the number of characters in the String. To count the number of occurrences, you must iterate over the entire String, plus iterate over them again to find the first one with a count of 1. In the worst case, you have no non-repeating characters, or the only non-repeating character is the last one. In either case, you have to iterate over the whole String once more. So it's O(n+n) = O(n).
EDIT
There is a bug in your code, by the way. Because you are using an insertion-order LinkedHashMap, each call to put(Character,Integer) results in a re-ordering of the underlying list. You should probably use a LinkedHashMap<Character,int[]> instead, and check for the presence of keys before putting. If they exist, then merely increment the value stored in the int[] to avoid re-ording the map by making another put call. Even so, the resulting list will be in reverse order from the way you iterate over it, so the first non-repeating character will be the last one you find when iterating over it whose value is 1. Alternatively, you could just iterate in reverse in your first for loop, then you avoid having to always go through the entire Entry set if the first non-repeating character comes sooner than the final character in the original String.

Check anagrams existence

Compare 2 Strings and return if they are anagrams or not.
I have a working code:
import java.util.*;
public class HelloWorld {
public static void main(String[] args) {
HashMap<String, Integer> map= new HashMap<>();
HashMap<String, Integer> map1= new HashMap<>();
String str1 = "abaa";
String str2 = "baaa";
String str3 = "bbbb"; //false
for(int i=0 ; i < str1.length(); i++){ //sr1 map
String value = String.valueOf(str1.charAt(i));
if (map1.containsKey(value)) {
map1.put(value, map1.get(value) + 1);
} else {
// No such key
map1.put(value, 1);
}
}
for(int i=0 ; i < str1.length(); i++){ //str2 map
String value = String.valueOf(str3.charAt(i));
if (map.containsKey(value)) {
map.put(value, map.get(value) + 1);
} else {
// No such key
map.put(value, 1);
}
}
if(map1.equals(map)){
System.out.println("true"); //anagrams
} else{
System.out.println("FalsE"); //not anagrams
}
}
}
It outputs TRUE for str1, str2 and FALSE for str1, str3 as it should.
I did this using hashmaps though, and I was wondering if this is efficient. How can I calculate the efficiency of this? What is a more efficient method?
Efficiency: Seems like 2 O(n) calls and the hashmap calls are all O(1). Explain?
The computational complexity of your implementation is O(n), assuming n is the number of characters for each string, all having the same length. You have two operations:
Creating a HashMap is O(n): For each of the n chars you do one lookup and one insert, which is O(1)
Comparing two HashMaps is also O(n): For each key in one, you look it up in the other and compare the two values, which is O(1)
Together, these operations still run in O(n). This assumes that your HashMap implementation does not have too many collisions for each bucket.
The complexity is O(n) in your case. There is no way the last hash compare is bigger than O(n), and the complexity could be in the worst case 3*O(n) that means O(n) per total.
I have a suggestion to improve your solution:
You can use a simple char[26] array instead of hashmap since you have only 26 letters.
Since your array has only 0 values, you only have to do ++array[String.valueOf(str1.charAt(i)) - 97] (no if/else required)
In the second for, you decrement for each character --array[String.valueOf(str1.charAt(i)) - 97] (no if/else required)
For the final step you go through the 26 items and you print "is not anagram" if you find array[i] != 0 and return...or print "is anagram" after this for
It's still O(n), but uses less memory I think and the last step is clearer..because you have O(27) constant, that is O(1)
Edited: I updated the code because I forgot you use java and not c++, sorry. 97 is the char value of 'a' used to normalize the letters from 97-122 to 0-25

How to get 2D array possible combinations

I have the following 2D array:
String[M][]
String[0]
"1","2","3"
String[1]
"A", "B"
.
.
.
String[M-1]
"!"
All the possible combinations should be in store in a resulting array
String[] combinations. So for example:
combinations[0] == {"1A....!")
combinations[1] == {"2A....!")
combinations[2] == {"3A....!")
combinations[3] == {"1B....!")
Notice that that the arrays are of variable length. Order of the elements in the output String doesn't matter. I also don't care if there are duplicates.
If the arrays were the same length, nested loops would do the trick, but they are not, and I really don't know how to approach the problem.
You can iterate through the combinations one at a time like clockwork by using an array to record the size of each inner array, and a counter array which keeps track of which member to use from each inner array. Something like this method:
/**
* Produce a List<String> which contains every combination which can be
* made by taking one String from each inner String array within the
* provided two-dimensional String array.
* #param twoDimStringArray a two-dimensional String array which contains
* String arrays of variable length.
* #return a List which contains every String which can be formed by taking
* one String from each String array within the specified two-dimensional
* array.
*/
public static List<String> combinations(String[][] twoDimStringArray) {
// keep track of the size of each inner String array
int sizeArray[] = new int[twoDimStringArray.length];
// keep track of the index of each inner String array which will be used
// to make the next combination
int counterArray[] = new int[twoDimStringArray.length];
// Discover the size of each inner array and populate sizeArray.
// Also calculate the total number of combinations possible using the
// inner String array sizes.
int totalCombinationCount = 1;
for(int i = 0; i < twoDimStringArray.length; ++i) {
sizeArray[i] = twoDimStringArray[i].length;
totalCombinationCount *= twoDimStringArray[i].length;
}
// Store the combinations in a List of String objects
List<String> combinationList = new ArrayList<String>(totalCombinationCount);
StringBuilder sb; // more efficient than String for concatenation
for (int countdown = totalCombinationCount; countdown > 0; --countdown) {
// Run through the inner arrays, grabbing the member from the index
// specified by the counterArray for each inner array, and build a
// combination string.
sb = new StringBuilder();
for(int i = 0; i < twoDimStringArray.length; ++i) {
sb.append(twoDimStringArray[i][counterArray[i]]);
}
combinationList.add(sb.toString()); // add new combination to list
// Now we need to increment the counterArray so that the next
// combination is taken on the next iteration of this loop.
for(int incIndex = twoDimStringArray.length - 1; incIndex >= 0; --incIndex) {
if(counterArray[incIndex] + 1 < sizeArray[incIndex]) {
++counterArray[incIndex];
// None of the indices of higher significance need to be
// incremented, so jump out of this for loop at this point.
break;
}
// The index at this position is at its max value, so zero it
// and continue this loop to increment the index which is more
// significant than this one.
counterArray[incIndex] = 0;
}
}
return combinationList;
}
How the method works
If you imagine the counter array being like a digital clock reading then the first String combination sees the counter array at all zeroes, so that the first String is made by taken the zero element (first member) of each inner array.
To get the next combination the counter array is incremented by one. So the least-significant counter index is increased by one. If this causes its value to become equal to the length of the inner array it represents then the index is zeroed, and the next index of greater significance is increased. A separate size array stores the length of each inner array, so that the counter array loop knows when an index has reached its maximum.
For example, if the size array was:
[3][3][2][1]
and the counter array was at:
[0][2][1][0]
then the increment would make the least significant (right-most) index equal to 1, which is its maximum value. So that index gets zeroed and the next index of greater significance (the second-from-right) gets increased to 2. But that is also the maximum of that index, so it gets zeroed and we move to the next index of greater significance. That gets increased to three, which is its maximum value so it gets zeroed and we move to the most significant (left-most) index. That gets increased to 1, which is less than its maximum so the incremented counter array becomes:
[1][0][0][0]
Which means the next String combination is made by taking the second member of the first inner array, and the first member of the next three inner arrays.
Dire warnings and notes
I wrote this just now in about forty minutes, and it's half-one in the morning, which means that even though it seems to do exactly what is needed, there are very likely bugs or bits of code which could be optimised. So be sure to unit test it thoroughly if its performance is critical.
Note that it returns a List rather than a String array because I think that Java Collections are vastly preferable to using arrays in most cases. Also, if you need a result set with no duplicates, you can simply change the List to a Set which will automatically drop duplicates and leave you with a unique set.
If you really need the result as a String array, don't forget you can use the List<String>.toArray(String[]) method to simply convert the returned List to what you need.
This problem has a very nice recursive structure to it (which also means it could explode in memory, the correct way should be using iterators such as the other answer, but this solution looks nicer imo and we can prove correctness inductively because of the recursive nature). A combination consists of an element from the first list attached to all possible combinations formed from the remaining (n-1) lists. The recursive work is done in AllCombinationsHelper, but you invoke AllCombinations. Note to test for empty lists and more extensively.
public static List<String> AllCombinations(List<List<Character>> aList) {
if(aList.size() == 0) { return new ArrayList<String>(); }
List<Character> myFirstSubList = aList.remove(0);
List<String> myStrings = new ArrayList<String>();
for(Character c : myFirstSubList) {
myStrings.add(c.toString());
}
return AllCombinationsHelper(aList, myStrings);
}
public static List<String> AllCombinationsHelper(List<List<Character>> aList,
List<String> aCollection) {
if(aList.size() == 0) { return aCollection; }
List<Character> myFirstList = aList.remove(0);
List<String> myReturnSet = new ArrayList<String>();
for(String s : aCollection) {
for(Character c : myFirstList) {
myReturnSet.add(c + s);
}
}
return AllCombinationsHelper(aList, myReturnSet);
}
Should be straight forward to do with recursion.
Let me rephrase a bit, so the terminology is less confusing.
We will call String[] as Token List, which is a list of Tokens
Now you have a List of Token List, you want to get one Token from each Token List available, and find out all combination.
What you need to do is, given a list of TokenList
If the List is having only one TokenList, the content of the Token List itself is all combinations
Else, make a sub-list by excluding the first Token List, and find out all combinations of that sub list. When you have the combinations, the answer is simply loop through your first token list, and generate all combinations using each token in the token list, and the result combinations.
I am only giving a psuedo code:
List<String> allCombinations(List<TokenList> listOfTokenList) {
if (length of strings == 1) {
return strings[0];
}
List<String> subListCombinations
= allCombination(listOfTokenList.subList(1)); // sublist from index 1 to the end
List<String> result;
for each (token in listOfTokenList[0]) {
for each (s in subListCombination) {
result.add(token + s);
}
}
return result;
}
I have been struggling with this problem for some time. But I finally solved it. My main obstacle was the SCOPE I used for declaring each variable. If you do not declare your variables in the correct scope, then the variable will retain changes made in the previous iteration.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class RecursiveAlgorithmTest {
private static int recursiveCallsCounter = 0;
public static ArrayList<ArrayList<String>> testCases = new ArrayList<ArrayList<String>>();
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
//set values for ArrayOfArrays
ArrayList<String> VariableA = new ArrayList<String>(Arrays.asList("red", "green"));
ArrayList<String> VariableB = new ArrayList<String>(Arrays.asList("A", "B", "C"));
ArrayList<String> VariableC = new ArrayList<String>(Arrays.asList("1", "2", "3", "4"));
ArrayList<ArrayList<String>> AofA = new ArrayList<ArrayList<String>>();
AofA.add(VariableA); AofA.add(VariableB); AofA.add(VariableC);
System.out.println("Array of Arrays: ToString(): " +AofA.toString());
ArrayList<String> optionsList = new ArrayList<String>();
//recursive call
recurse(optionsList, AofA, 0);
for (int i = 0 ; i < testCases.size() ; i++) {
System.out.println("Test Case " + (i+1) + ": " + testCases.get(i));
}
}//end main(String args[])
private static void recurse(ArrayList<String> newOptionsList,
ArrayList<ArrayList<String>> newAofA, int placeHolder){
recursiveCallsCounter++;
System.out.println("\n\tStart of Recursive Call: " + recursiveCallsCounter);
System.out.println("\tOptionsList: " + newOptionsList.toString());
System.out.println("\tAofA: " + newAofA.toString());
System.out.println("\tPlaceHolder: "+ placeHolder);
//check to see if we are at the end of all TestAspects
if(placeHolder < newAofA.size()){
//remove the first item in the ArrayOfArrays
ArrayList<String> currentAspectsOptions = newAofA.get(placeHolder);
//iterate through the popped off options
for (int i=0 ; i<currentAspectsOptions.size();i++){
ArrayList<String> newOptions = new ArrayList<String>();
//add all the passed in options to the new object to pass on
for (int j=0 ; j < newOptionsList.size();j++) {
newOptions.add(newOptionsList.get(j));
}
newOptions.add(currentAspectsOptions.get(i));
int newPlaceHolder = placeHolder + 1;
recurse(newOptions,newAofA, newPlaceHolder);
}
} else { // no more arrays to pop off
ArrayList<String> newTestCase = new ArrayList<String>();
for (int i=0; i < newOptionsList.size();i++){
newTestCase.add(newOptionsList.get(i));
}
System.out.println("\t### Adding: "+newTestCase.toString());
testCases.add(newTestCase);
}
}//end recursive helper
}// end of test class
In Python one uses itertools.product and argument unpacking (apply)
>>> import itertools
>>> S=[['1','2','3'],['A','B'],['!']]
>>> ["".join(x) for x in itertools.product(*S)]
['1A!', '1B!', '2A!', '2B!', '3A!', '3B!']

Categories