I am reading a code snippet and I am wondering why the following could
check if str1 and str2 are anagram.
boolean result = str1.chars().allMatch( c1 -> str2.chars()
.anyMatch( c2 -> c1 == c2) );
I am particularly confused about
if str1 = "abc" str2 = "cbad", the result should be also true, right?
I am particularly confused about if str1 = "abc" str2 = "cbad", the result should be also true, right?
The code will return true, yes. So no, it's not a valid check for an anagram, since in the example you cite, it will say it's an anagram when it's not (since str1 doesn't have d).
In an anagram, both strings would need to have the same number of occurrences of the same letters. One byproduct of that is that they would need to be the same length (although in some anagram schemes, you're allowed to vary the number of spaces and add/remove punctuation).
So a valid check would need to ensure that every character in str1 (perhaps ignoring spaces and punctuation) consumed a character from str2, and that there were no characters from str2 left at the end (perhaps ignoring spaces and punctuation). There are lots of ways to do that (count the occurrences in str1, perhaps in a Map, and then count down matches from str2; have a string you remove letters from, etc.) The quoted code just isn't one of them.
If you have str1 = "abc" and str2 = "cbad", they cannot be anagram of each other because str2 contains the extra character d which str1 does not have. For one word to be an anagram of the other word, they each needs to have the same character with the same number of frequencies.
That being said, I just ran the code you have written and it does not work for that specific case that you have mentioned.
One way you can determine if one string is an anagram of another string is by using two HashMap. For each strings, loop through it, track each character as the key and its frequencies as value. At the end, you can loop through both of the HashMap and keep comparing the value for each key. If the values are not equal, return false.
Also first thing to check is that the length of both strings should be equal. If they fail that condition, they cannot be an anagram then.
The following is the code I wrote on LeetCode many months ago. This can definitely be optimized and made much more cleaner but just to give you an idea.
public class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
HashMap<Character, Integer> tmap = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (map.containsKey(c)) {
int count = map.get(c);
map.put(c, count + 1);
}
else {
map.put(c, 1);
}
}
for (int j = 0; j < t.length(); j++) {
char d = t.charAt(j);
if (!(map.containsKey(d))) {
return false;
}
if (tmap.containsKey(d)) {
int tracker = tmap.get(d);
tmap.put(d, tracker + 1);
}
else {
tmap.put(d, 1);
}
}
for (Character key: map.keySet()) {
int mapv = map.get(key);
int tmapv = tmap.get(key);
if (mapv != tmapv) {
System.out.println(tmapv);
System.out.println(mapv);
return false;
}
}
return true;
}
}
Based on T.J. Crowder's answer this should be a possible way to check for an anagram:
public static boolean isAnagram(String subject, String possibleAnagram, IntPredicate isAnagramCodePoint)
{
Objects.requireNonNull(subject);
Objects.requireNonNull(possibleAnagram);
Objects.requireNonNull(isAnagramCodePoint);
// Convert inputs to valid anagram letters.
int[] subjectLetters = subject.codePoints()
.filter(isAnagramCodePoint)
.toArray();
int[] possibleAnagramLetters = possibleAnagram.codePoints()
.filter(isAnagramCodePoint)
.toArray();
// Permutation = Same number of occurrences of the same letter.
if (subjectLetters.length != possibleAnagramLetters.length)
{
return false;
}
HashMap<Integer, Integer> occurrences = new HashMap<>(subjectLetters.length);
// Count (produce) number of occurrences for each letter in subject.
for (int codePoint : subjectLetters)
{
occurrences.merge(codePoint, 1, (a, b) -> a + b);
}
// Consume each letter occurrence from subjectLetters.
for (int codePoint : possibleAnagramLetters)
{
int val = occurrences.merge(codePoint, -1, (a, b) -> a + b);
if (val == -1)
{
return false; // Current letter occurred more often in possibleAnagram
}
}
// Check equal number of occurrences for the same letter.
for (int number : occurrences.values())
{
if (number != 0)
{
return false;
}
}
return true;
}
private static final IntPredicate COMPLETE = (codePoint) -> true;
public static boolean isAnagram(String subject, String possibleAnagram)
{
return isAnagram(subject, possibleAnagram, COMPLETE);
}
The optional IntPredicate is used to specify the anagram schema, a possible value could be Character::isLetter to ignore all code points except letters, Character::isDigit, etc.
Related
How do you compare a palindromic word to one of the newly formed words of an anagram?
And how do you grab one of the newly formed words for it to be compared to the input word?
This is my code:
public class SampleCode2 {
public static boolean isPalindromic(String word, String mark) {
if (word.length() == 0) {
}
for (int i = 0; i < word.length(); i++) {
String newMark = mark + word.charAt(i);
String newLetters = word.substring(0, i) +
word.substring(i + 1);
}
String ifPalindrome = ""; //will store here the reversed string
String original = word; //will store here the original input word
//to reverse the string
for (int i = word.length() - 1; i >= 0; i--) {
ifPalindrome += word.charAt(i);
}
//to compare the reversed string to the anagram
if (word.equals(ifPalindrome)) {
return true;
} else {
return false;
}
}
public static void main(String[] args) {
boolean check = isPalindromic("mmaad", "");
System.out.println(check);
}
}
It's not yet done because the permutation and comparison won't work. The output is displaying false, I need it to be true because the anagram of MMAAD is madam. And I have to check if madam is indeed a palindrome of mmaad.
So What I did is used HashMap instead of creating words from the given word
A String can be of even or odd length
If "EVEN" String is palindrome then every "character" in the String will appear even times eg: String str = maam : m=2, a=2
If "ODD" String is a palindrome then there will only be 1 character of odd occurrence and the rest will occur even times.
eg: String str = mmaad: m=2,a=2,d=1
To store the Occurrence of the Characters in the String we will use HashMap where Character of the String is the KEY and its occurrence is VALUE
HashMap<Character,Integer> mapChar = new HashMap<>();
And we will add each Character in the HashMap with the number of times it has appeared in the String.
Now we will check if the String length is "even" or "odd"
if "EVEN" Length String we know that every character will appear EVEN times and if any time a character appears "ODD" times we return false i.e It's not a Palindrome
for (Map.Entry<Character, Integer> entries : mapChar.entrySet()) {
if (entries.getValue() % 2 != 0) {
return false;
}
}
If "ODD" Length String we know that only one Character will appear odd time and the rest will be of EVEN Occurrence
And if there are 2 characters that occur odd times then its not a palindrome
// Number of times odd value Character as occurred
int occur1 = 0;
for (Map.Entry<Character, Integer> entries : mapChar.entrySet()) {
if (entries.getValue() % 2 == 1) {
occur1++;
if (occur1 > 1) {
return false;
}
}
}
Here's the whole Code:
public static void main(String[] args) throws Exception {
boolean check = isPalindromic("malayalam", "");
System.out.println(check);
}
public static boolean isPalindromic(String word, String mark) {
boolean isPal = true;
if (word.length() == 0) {
return false;
}
HashMap<Character, Integer> mapChar = new HashMap<>();
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
if (mapChar.containsKey(ch)) {
mapChar.put(ch, mapChar.get(ch) + 1);
} else {
mapChar.put(ch, 1);
}
}
if (word.length() % 2 == 0) {
for (Map.Entry<Character, Integer> entries : mapChar.entrySet()) {
if (entries.getValue() % 2 != 0) {
return false;
}
}
} else {
int occur1 = 0;
for (Map.Entry<Character, Integer> entries : mapChar.entrySet()) {
if (entries.getValue() % 2 == 1) {
occur1++;
if (occur1 > 1) {
isPal = false;
break;
}
}
}
}
return isPal;
}
output:
mmaa
Is Palindrome: true
mmaad
Is Palindrome: true
niti
Is Palindrome: false
If you want to find a list of anagrams of a word that are palindromes, you can split this task into two parts: first get a list of the character permutations of that word List<String>, i.e. a list of anagrams, and then filter those strings that are palindromes. For example, for the mmaad string, the palindromes are:
madam
amdma
Getting a list of permutations is an expensive operation for large strings, since the number of permutations is a factorial of the string length. For example, for the mmaad string, there are 120 permutations. Filtering palindromes after that is cheaper.
Try it online!
public static void main(String[] args) {
// list of distinct permutations
getPermutations("mmaad")
// Stream<String>
.stream()
// filter palindromes
.filter(str -> isPalindrome(str))
// output
.forEach(System.out::println);
}
/**
* #param str source string, may contain surrogate pairs.
* #return whether the source string is a palindrome.
*/
public static boolean isPalindrome(String str) {
// array of characters of the string
int[] chars = str.codePoints().toArray();
return IntStream
// iterate from the beginning to the middle of the string
.range(0, chars.length / 2)
// compare the characters: first - last, second - penultimate
.allMatch(i -> chars[i] == chars[chars.length - i - 1]);
}
/**
* #param str source string, may contain surrogate pairs.
* #return a list of distinct permutations of characters of the source string.
*/
public static List<String> getPermutations(String str) {
// array of characters of the string
int[] chars = str.codePoints().toArray();
return IntStream.range(0, chars.length)
// Stream<List<Map<Integer,String>>>
.mapToObj(i -> IntStream.range(0, chars.length)
// represent each character as Map<Integer,String>
.mapToObj(j -> Map.of(j, Character.toString(chars[j])))
// collect a list of maps
.collect(Collectors.toList()))
// reduce a stream of lists to a single list
.reduce((list1, list2) -> list1.stream()
// summation of pairs of elements,
// i.e. maps, from two lists
.flatMap(map1 -> list2.stream()
// filter out those keys
// that are already present
.filter(map2 -> map2.keySet().stream()
.noneMatch(map1::containsKey))
// join entries of two maps
.map(map2 -> {
Map<Integer, String> map =
new LinkedHashMap<>();
map.putAll(map1);
map.putAll(map2);
return map;
}))
// collect into a single list
.collect(Collectors.toList()))
// List<Map<Integer,String>>
.orElse(List.of(Map.of(0, str)))
// Stream<Map<Integer,String>>
.stream()
// map of strings to a single string
.map(map -> String.join("", map.values()))
.distinct()
// list of distinct permutations
.collect(Collectors.toList());
}
See also:
• How to create all permutations of tuples without mixing them?
• Reverse string printing method
I've written code to find the amount of duplicated characters in a string input. However after reading the specification, I realise this isn't what is being asked. Yes, I need to count the duplicated characters, but not the amount of that particular character, I need the amount of all characters being duplicated.
I've given an input and output to help explain better. I don't think I'm doing a good job.
Input:
"abcdea" => "a" is duplicated =>
Output:
1
Input:
"abcdeae" => "a" and "e" is duplicated =>
Output:
2
Input:
"abcdeaed" => "a", "d" and "e" is duplicated =>
Output:
3
I've put my code below. Can anyone help adjust my code please?
public static int duplicatesCount(String text)
{
Map<Character,Integer> map = new HashMap<Character,Integer>();
char[] carray = text.toCharArray();
for (char c : carray)
{
if (map.containsKey(c))
{
map.put(c, map.get(c) +1);
}
else
{
map.put(c, 1);
}
}
Set <Character> setChar = map.keySet();
int returnC = 1;
for (Character c : setChar)
{
if (map.get(c) > 1)
{
returnC = map.get(c);
}
}
return returnC;
A better way to do this is to sort the string and then iterate through it. If the previous character = the current character, you increase the duplicate number and don't increment it again util you see the character change.
This requires no extra storage (e.g. the hash map).
This can also be used to count the number of duplicates of each letter with a minor change :).
At the end of your snippet...
int returnC = 0;
for (Character c : setChar)
{
if (map.get(c) > 1)
{
returnC ++; //for each which has more than one instance
}
}
return returnC;
Just count everytime you come across a character that has a value greater than 1 in your hashmap
int returnC = 0;
for (Character c : setChar)
{
if (map.get(c) > 1)
returnC++;
}
Or you can do it while you're creating your hashmap like this
Map<Character,Integer> map = new HashMap<Character,Integer>();
char[] carray = text.toCharArray();
Set <Character> setChar = new Set<Character>(); //initialize the set up here
for (char c : carray)
{
if (map.containsKey(c))
{
map.put(c, map.get(c) +1);
setChar.add(c); //just add to set when a char already exists
}
else
{
map.put(c, 1);
}
}
return setChar.size(); //then simply return the size of the set
Here is another approach - without using Map or sorting algorithm.
public static int duplicatesCount(String text) {
int cnt = 0;
while (text.length() > 1) {
char firstLetter = text.charAt(0);
if (text.lastIndexOf(firstLetter) > 0) {
cnt++;
text = text.replaceAll(("" + firstLetter), "");
} else {
text = text.substring(1, text.length());
}
}
return cnt;
}
The basic idea is to go through a string, char-by-char and check does the character exist somewhere in the string ( if (text.lastIndexOf(firstLetter) > 0)). If it exists increment cnt and then remove all occurrences of the character in the string.
Otherwise, just remove the fist character (text = text.substring(1, text.length());)
You could use String methods
int count = 0;
String s = "abcdeabc";
while(s.length() > 0) {
String c = s.substring(0, 1);
if(s.lastIndexOf(c) != 0) {
count++;
}
s = s.replaceAll(c, "");
}
In your current code you are returning the value not the count.
For Eg:
If your input was "abcdea", then returnC holds the value 2 which is the
number of times the key a has repeated.
If your input was "abcdabeb" where a is repeated twice and b is repeated
thrice. Your output will be 3, as returnC will be holding the value for the key
b.
You can modify your code like this.
Set <Character> setChar = map.keySet();
int returnC = 0;
for (Character c : setChar) {
if (map.get(c) > 1) {
returnC++;
}
}
return returnC;
Also we can user String Buffer class to to create another string of duplicate charracters-- and then we can display it--
public static void main(String arghya[])
{
String name="ARGHYA MUKHERJEE";
StringBuffer duplicate=new StringBuffer();
Set<Character> charlist=new HashSet<Character>();
for(int i=0;i<name.length();i++) {
Character c=name.charAt(i);
if(charlist.contains(c))
{
duplicate.append(c);
//System.out.println(c+ " is repeatitive character in the string");
}
else {
charlist.add(c);
}
}
System.out.print("Duplicate characters which has appeared are as follows: "+duplicate);
}
I have written a Java program to find Anagram for 2 strings.
For Reference:
Two strings are anagrams if they are written using the same exact letters, ignoring space, punctuation and capitalization. Each letter should have the same count in both strings. For example, Army and Mary are anagram of each other.
Program:
package practice;
import java.util.ArrayList;
import java.util.List;
public class Anagram_String {
public static void main(String[] args) {
String s1="mary";
String s2="army";
int k=0;
List<String> matchedChar= new ArrayList<String>();
String charmatch="";
char[] ch1= s1.toLowerCase().toCharArray();
char[] ch2= s2.toLowerCase().toCharArray();
if(s1.length()==s2.length())
{
for(int i=0;i<s1.length();i++)
{
for(int j=0;j<s2.length();j++)
{
if(ch1[i]==ch2[j])
{
k++;
charmatch=String.valueOf(ch1[i]);
System.out.println(charmatch);
matchedChar.add(charmatch);
System.out.println("Arraylist value is "+matchedChar.toString());
System.out.println(matchedChar.size());
}
}
k=0;
}
String arrayValue=matchedChar.toString();
System.out.println("Array value is "+arrayValue);
if(arrayValue.contains(s2)){
System.out.println("String 1 and String 2 are anagrams of each other");
}
else
{
System.out.println("String 1 and String 2 are not anagrams of each other");
}
}
}
}
Output:
m
Arraylist value is [m]
1
a
Arraylist value is [m, a]
2
r
Arraylist value is [m, a, r]
3
y
Arraylist value is [m, a, r, y]
4
Array value is [m, a, r, y]
String 1 and String 2 are not anagrams of each other
Here if you see all the characters are added to to the arraylist but when compared with the string, it is showing the output as they are not anagrams of each other.
Kindly help me to find solution for this.
Thank you,
What I think is that your solution will work only for words with unique characters, and time complexity will be O(n^2) (where n - is the length of String).
However, there is a better solution for such problem:
Take String.toCharArray() value for each string
Sort those arrays
If those arrays are equal, then your words are anagrams
You can count number of letters in both strings. If both strings have the same number of letters they are anagrams.
You can use an int[] to store number of letters.
public static boolean anagrams(String a, String b) {
int[] letters = new int[26];
// Convert to upper case because the test is case insensitive
a = a.toUpperCase();
b = b.toUpperCase();
for (int i = 0; i < a.length(); i++) {
char ch = a.charAt(i);
if (ch < 'A' || ch > 'Z') {
continue; // Skip non letters
}
letters[ch - 'A']++; // Increment number of the current letter
}
for (int i = 0; i < b.length(); i++) {
char ch = b.charAt(i);
if (ch < 'A' || ch > 'Z') {
continue; // Skip non letters
}
letters[ch - 'A']--; // Decrement number of the current letter
}
for (int i = 0; i < letters.length; i++) {
if (letters[i] != 0) {
// If there are more or less of this letter
// in the first string it is not an anagram
return false;
}
}
return true;
}
Note this algorithm is done in O(n) where n is the number of letters of each string. Sorting the strings needs at least O(n log(n))
Taking the idea from AxelH's comments it is possible to create an external method to loop as follow.
private void countLetters(int[] letters, String str, int incrementFactor) {
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch < 'A' || ch > 'Z') {
continue; // Skip non letters
}
letters[ch - 'A'] += incrementFactor;
}
}
public static boolean anagrams(String a, String b) {
int[] letters = new int[26];
countLetters(letters, a.toUpperCase(), 1); // Note the +1
countLetters(letters, b.toUpperCase(), -1); // Note the -1
for (int i = 0; i < letters.length; i++) {
if (letters[i] != 0) {
// If there are more or less of this letter
// in the first string it is not an anagram
return false;
}
}
return true;
}
This seems to me a more readable and elegant way. Thanks AxelH.
Note In the previous code there are expressions like letters[ch - 'A']++. This line of code use an interesting properties of type char of java that is a special primitive numeric type, so it is possible to use mathematical operations on it.
In particular:
'A' - 'A' --> 0
'B' - 'A' --> 1
'C' - 'A' --> 2
'D' - 'A' --> 3
...
'Z' - 'A' --> 25
So this expression can be used to give an index to a letter starting from 0 for A ending to 25 for Z.
My answer is quite similar to Marine's, but takes a little higher-level approach with Java 8 streams, making the code a little more concise and readable:
public class Application {
public boolean isAnagramsEqual(String str1, String str2) {
Map<Character, Long> count = countChars(str1);
Map<Character, Long> count2 = countChars(str2);
return count.equals(count2);
}
private Map<Character, Long> countChars(String str) {
return str.toLowerCase()
.chars().mapToObj(ch -> (char) ch) //convert into stream of Characters
.filter(Character::isLetterOrDigit) //filter out not-needed chars
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}}
Method countChars creates a map with each unique character mapped to it's count in the given string.
It may be a little less performant than Marine's, but it's still O(n).
Your outputs says it itself:
Array value is [m, a, r, y]
As mentioned above I would also just create array and sort them, but here is the solution you may be searching for:
String s1="mary";
String s2="army";
List<String> matchedChar= new ArrayList<String>();
String charmatch="";
char[] ch1= s1.toLowerCase().toCharArray();
char[] ch2= s2.toLowerCase().toCharArray();
if(s1.length()==s2.length())
{
for(int i=0;i<s1.length();i++)
{
for(int j=0;j<s2.length();j++)
{
if(ch1[i]==ch2[j])
{
charmatch=String.valueOf(ch1[i]);
System.out.println(charmatch);
matchedChar.add(charmatch);
System.out.println("Arraylist value is "+matchedChar.toString());
System.out.println(matchedChar.size());
}
}
}
String arrayValue="";
for (String s : matchedChar){
arrayValue = arrayValue + s;
}
System.out.println("Array value is "+arrayValue);
System.out.println("s1 value is "+s1);
if(arrayValue.equals(s1)){
System.out.println("String 1 and String 2 are anagrams of each other");
}
else
{
System.out.println("String 1 and String 2 are not anagrams of each other");
}
}
Use .split("(?!^)") on your String to make it an String Array. Next sort arrays and compare it. It's the best option for me too.
This is how you can do it by sorting the arrays:
public static void main(String[] args) {
System.out.println(isAnagram("mary", "army"));
}
public static boolean isAnagram(String s1, String s2){
char[] c1 = s1.toLowerCase().toCharArray();
char[] c2 = s2.toLowerCase().toCharArray();
Arrays.sort(c1);
Arrays.sort(c2);
boolean anagram = false;
if(Arrays.equals(c1, c2)){anagram = true;}
return anagram;
}
In this code i converted my string into char array using the code :String.toCharArray() inside a function named toSort() and sorted the words in the string. Then inside Isanagram() method I checked whether it is anagram or not. For that first I have to make sure whether the sorted strings are of same length or not after I compared each character in one string with other.
Here is the full code try to decompose each method and study.
import java.util.Scanner;
public class StANaEx1 {
String toSort(String s5){
int i,j;
char temp;
char ch1[] = s5.toCharArray();
int len = ch1.length;
for(i=0;i<len;i++){
for(j=i+1;j<len;j++){
if(ch1[i]>ch1[j]){
temp = ch1[i];
ch1[i] = ch1[j];
ch1[j] = temp;
}
}
}
String s6 = new String(ch1);
return s6;
}
void isAnagram(String s9,String s10){
int i,len1,len2,flag=0;
System.out.println("s9 : "+s9);
System.out.println("s10 : "+s10);
System.out.println("");
s9 = s9.trim(); //To remove white spaces again no need.I used because my compiler didn't recognize my replace() method in main() method.
s10 = s10.trim();
len1 = s9.length();
len2 = s10.length();
System.out.println("len1 : "+len1); //To check the length of the given strings without white spaces.
System.out.println("len2 : "+len2);
System.out.println("");
for(i=0;i<len1;i++){
System.out.println("s9["+i+"] : "+s9.charAt(i)); //Error checking.
}
System.out.println("");
for(i=0;i<len2;i++){
System.out.println("s10["+i+"] : "+s10.charAt(i));
}
System.out.println("");
if(len1!=len2){
System.out.println("Not Anagram string length different");
}
else{
for(i=0;i<len1;i++){ //Since string lengths are same len1 = len2.
if(s9.charAt(i)!=s10.charAt(i)){
flag=1;
break;
}
}
if(flag==0){
System.out.println("Anagram");
}
else{
System.out.println("Not Anagram");
}
}
}
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
StANaEx1 ob1 = new StANaEx1();
System.out.println("-----Anagram checking-----");
System.out.println("");
System.out.println("Enter the 1st String: ");
System.out.println("");
String s1 = sc.nextLine();
s1 = s1.replace("//s", ""); //This is to remove white spaces.
String s3 = s1.toLowerCase(); //Change the input string to lower case in order to make sorting easy.
System.out.println("");
System.out.println("Enter the next String: ");
String s2 = sc.nextLine();
s2 = s2.replace("//s", "");
String s4 = s2.toLowerCase();
System.out.println("");
String s7 = ob1.toSort(s3);
String s8 = ob1.toSort(s4);
ob1.isAnagram(s7,s8);
sc.close();
}
}
Output
-----Anagram checking-----
Enter the 1st String:
Mary
Enter the next String:
army
s9 : amry
s10 : amry
len1 : 4
len2 : 4
s9[0] : a
s9[1] : m
s9[2] : r
s9[3] : y
s10[0] : a
s10[1] : m
s10[2] : r
s10[3] : y
Anagram
Output 2
-----Anagram checking-----
Enter the 1st String:
Sniper
Enter the next String:
kill
s9 : einprs
s10 : ikll
len1 : 6
len2 : 4
s9[0] : e
s9[1] : i
s9[2] : n
s9[3] : p
s9[4] : r
s9[5] : s
s10[0] : i
s10[1] : k
s10[2] : l
s10[3] : l
Not Anagram string length different
import java.util.Arrays;
public class AnagramString {
public static void main(String[] args) {
String str1="Keep";
String str2="peek";
//convert the string into the array with lower casing its character
char arrstr1[]=str1.toLowerCase().toCharArray();
char arrstr2[]=str2.toLowerCase().toCharArray();
//sort the array of character by acending order
Arrays.sort(arrstr1);
Arrays.sort(arrstr2);
//set true boolean value to the status
boolean status=true;
//comparing the char array
status = Arrays.equals(arrstr1, arrstr2);//here we get true value if they are containing the same character
System.out.println(Arrays.toString(arrstr1));
System.out.println(Arrays.toString(arrstr2));
if(arrstr1.length==arrstr2.length && status) {
System.out.println("2 string are anagram");
}else {
System.out.println("2 string are not anagram");
}
}
}
The problem asks to "implement an algorithm to determine if a string has all unique character.
I saw the solution, but don't quite understand.
public boolean isUniqueChars(String str) {
if (str.length() > 256) return false;
boolean[] char_set = new boolean[256];
for (int i = 0; i < str.length(); i++) {
int val = str.charAt(i);
if (char_set[val])
return false;
char_set[val] = true;
}
return true;
}
Do we not use parseInt or (int) converter in front of the code? (Will str.charAt[i] be automatically changed to int?)
What does boolean[] char set=new boolean[256] mean?
Why do we need to set char_set[val]=true?
We can also use HashSet Data structure to determine if string has all unique characters in java.
Set testSet = new HashSet();
for (int i = 0; i < str.length(); i++) {
testSet.add(new Character(str.charAt(i)));
}
if (testSet.size() == str.length()) {
System.out.println("All charcaters are Unique");
} else {
System.out.println("All charcaters are niot unique");
}
See my explanation in the comments, since you only tagged algorithm I'm assuming no language and just address the algorithm itself:
public boolean isUniqueChars(String str){
//more than 256 chars means at least one is not unique
//please see first comment by #Domagoj as to why 256 length
if(str.length()>256) return false;
//keeping an array to see which chars have been used
boolean[] char_set = new boolean[256];
//iterating over the string
for(int i=0; i<str,length;i++){
//not sure what language this is, but let's say it returns an
//int representation of the char
int val=str.charAt(i);
//meaning this has been set to true before, so this char is not unique
if(char_set[val])
//nope, not unique
return false;
//remember this char for next time
char_set[val]=true;
}
//if we reached here, then string is unique
return true;
}
A simple solution would be to convert String to a Set and compare the lengths of corresponding objects. Use java 8:
private static boolean checkUniqueChars(String copy) {
if(copy.length() <= 1) return true;
Set<Character> set = copy.chars()
.mapToObj(e->(char)e).collect(Collectors.toSet());
if (set.size() < copy.length()){
return false;
}
return true;
}
One way you can do is via bits.
Time Complexity : O(N) (You could also argue the time complexity is 0(1), since the for loop will never iterate through more than
128 characters.)
Space Complexity : O(1)
Consider each char as bit(whether its there or not). Example, we need to check if all the chars are unique in "abcada", so if we will check if the bit for a char is already turned on, if yes then we return false otherwise set the bit there.
Now how we do it ? All the chars can be represented as numbers and then bits. We are gonna use "set bit" and "get bit" approach.
We will assume, in the below code, that the string only uses the lowercase letters a through z.
public static boolean isUniqueChars(String str) {
int mask = 0;
for (int i = 0; i < str.length(); ++i) {
int val = str.charAt(i) - 'a'; // you will get value as 0, 1, 2.. consider these as the positions inside your mask which you need to check if the bit is set or not
if ((mask & (1 << val)) > 0) return false; // Check if the bit is already set
mask |= (1 << val); // Set bit
}
return true;
}
To understand bit manipulation , you can refer
https://www.hackerearth.com/practice/notes/bit-manipulation/
https://snook.ca/archives/javascript/creative-use-bitwise-operators
It took me a while to understand bits but once I did, it was eye opening. You can solve so many problems using bits and provides most optimal solutions.
Think about how you would do this with a paper and pencil.
Write out the alphabet once.
Then go through your string character by character.
When you reach a character cross it out of your alphabet.
If you go to cross out a character and find that it has already been crossed out, then you know the character appeared previously in your string and you can then stop.
That's essentially what the code you posted does, using an array. The operation completes in O(N) time with O(K) extra space (where K is the number of keys you have).
If your input had a large number of elements or you could not know what they were ahead of time, you could use a hash table to keep track of which elements have already been seen. This again takes O(N) time with O(cK) extra space, where K is the number of keys and c is some value greater than 1.
But hash tables can take up quite a bit of space. There's another way to do this. Sort your array, which will take O(N log N) time but which requires no extra space. Then walk through the array checking to see if any two neighbouring characters are the same. If so, you have a duplicate.
You could see the detailed explanation in my blogpost here:
Check if string has all unique characters
The simplest solution is to make a loop through all characters, use hashMap and to put each character into the hashmap table, and before this check if the character is already there. If the character is already there, it's not unique.
public class UniqueString {
public static void main(String[] args) {
String input = "tes";
Map<String, Integer> map = new HashMap<String, Integer>();
for (int i = 0; i < input.length(); i++) {
if (!map.containsKey(Character.toString(input.charAt(i)))) {
map.put(Character.toString(input.charAt(i)), 1);
} else {
System.out.println("String has duplicate char");
break;
}
}
}
}
Java SE 9
You can simply match the length of the string with the count of distinct elements. In order to get the IntStream of all characters, you can use String#chars on which you can apply Stream#distinct to get the Stream of unique elements. Make sure to convert the string to a single case (upper/lower) otherwise the function, Stream#distinct will fail to count the same character in different cases (e.g. I and i) as one.
Demo:
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// Test
Stream.of(
"Hello",
"Hi",
"Bye",
"India"
).forEach(s -> System.out.println(s + " => " + hasUniqueChars(s)));
}
static boolean hasUniqueChars(String str) {
return str.toLowerCase().chars().distinct().count() == str.length();
}
}
Output:
Hello => false
Hi => true
Bye => true
India => false
Java SE 8
static boolean hasUniqueChars(String str) {
return Arrays.stream(str.toLowerCase().split("")).distinct().count() == str.length();
}
For best performance, you should use a Set, and add the characters of the string to the set. If the set.add(...) method returns false, it means that the given character has been seen before, so you return false, otherwise you return true after adding all the characters.
For the simple solution, use Set<Character>:
public static boolean allUniqueCharacters(String input) {
Set<Character> unique = new HashSet<>();
for (int i = 0; i < input.length(); i++)
if (! unique.add(input.charAt(i)))
return false;
return true;
}
That will however not handle Unicode characters outside the BMP, like Emojis, so we might want to change the set to use Unicode Code Points:
public static boolean allUniqueCodePoints(String input) {
Set<Integer> unique = new HashSet<>();
return input.codePoints().noneMatch(cp -> ! unique.add(cp));
}
However, even Code Points do not represent "characters" as we humans think of them. For that we need to process Grapheme Clusters:
public static boolean allUniqueClusters(String input) {
BreakIterator breakIterator = BreakIterator.getCharacterInstance(Locale.US);
breakIterator.setText(input);
Set<String> unique = new HashSet<>();
for (int start = 0, end; (end = breakIterator.next()) != BreakIterator.DONE; start = end)
if (! unique.add(input.substring(start, end)))
return false;
return true;
}
Or with Java 9+:
public static boolean allUniqueClusters(String input) {
Set<String> unique = new HashSet<>();
return Pattern.compile("\\X").matcher(input).results()
.noneMatch(m -> ! unique.add(m.group()));
}
In JS,
const isStringUnique = str => {
if(str){
let obj = {}
for(let char of str){
obj[char] ? obj[char]++ : obj[char]=1;
}
for(let char of str){
if(obj[char] > 1)
return false;
}
return true;
}
return true;
}
public class CheckStringUniqueChars {
public static boolean checkUnique(String str) {
int i=0,j=str.length()-1;
while(i<j) {
if(str.charAt(i) == str.charAt(j)) {
return false;
}
i++;
j--;
}
return true;
}
}
Given a List of Strings and an array of characters, return the longest String that contains only characters in the array.
I'm pretty sure I hosed it up. My first instinct was to use a regular expression but I don't think anybody gets those right the first time and without looking anything up.
Is there a tricky way of doing this using bitwise operators or something?
One idea would be to convert the char[] to a Set<Character> for O(1) containment tests, then simply loop over the list of strings and check if each particular string has only characters contained in the aforementioned set, keeping track of the longest string you find with this property.
If you have more information, you could make more optimizations. For example, if the strings themselves are very long but the list isn't, it might be beneficial to sort the list by length first, then start processing the strings longest first.
Is there a tricky way of doing this using bitwise operators or something?
If you have some sort of (small-ish) limit on the range of character that can be included in the char[], then you could potentially encode the whole thing in a single int/long, which would be a substitute for the Set<Character> I mentioned above. For example, let's say that only the characters from 'a' to 'z' will be included, then we can perform the encoding as follows:
long charset = 0;
for (char c : chars) {
charset |= (1 << (c - 'a'));
}
Now, to check if some character c was contained in the original char[], we can simply use:
if ((charset & (1 << (c - 'a'))) != 0) {
// c was in the original char[]
}
The following code uses binary search on a sorted char array to efficiently check, if all characters of the string exist in the char[]. Note that binary search on an array is pretty fast due to cache locality.
public String longest(char[] chars, List<String> strings) {
char[] sorted = Arrays.copyOf(chars, chars.length);
Arrays.sort(sorted);
String result = null;
for (String string : strings) {
if (containsAll(sorted, string)
&& (result == null || string.length() > result.length())) {
result = string;
}
}
return result;
}
public boolean containsAll(char[] sorted, String string) {
int length = string.length();
for (int i = 0; i < length; ++i) {
if (Arrays.binarySearch(sorted, string.charAt(i)) < 0) {
return false;
}
}
return true;
}
arshajii's first solution implementation:
/**
* #param list List of Strings.
* #param array Array of characters.
* #return The longest String from list that contains only characters in the array.
* If there is no such String, "" will be returned.
*/
private static String getLongest(List<String> list, Character[] array) {
String longest = "";
if (list == null || list.isEmpty() || array == null
|| array.length == 0) {
return longest;
}
Set<Character> set = new HashSet<Character>(Arrays.asList(array));
for (String word : list) {
boolean valid = true;
for (Character c : word.toCharArray()) {
if (!set.contains(c)) {
valid = false;
break;
}
}
if (valid && longest.length() < word.length()) {
longest = word;
}
}
return longest;
}
In case you are allowed to use Java 8:
public static Optional<String> longestValidWord(List<String> words, char[] validCharacters){
String allValidCharacters = new String(validCharacters);
return words.stream()
.filter(word -> word.chars().allMatch(c -> allValidCharacters.indexOf(c) > -1))
.max((s1, s2) -> Integer.compare(s1.length(), s2.length()));
}