I am tring this method to find the common characters in two stringBuffers by returning a stringBuffer. without using arrays. Please tell me what errors I am making.
private StringBuffer stringBuffer1;
private StringBuffer stringBuffer2;
public commonCharacters(String s1, String s2) {
stringBuffer1 = new StringBuffer(s1);
stringBuffer2 = new StringBuffer(word2);
}
String commonChars = "";
for (i = 0; i < stringBuffer1.length; i++) {
char c = stringBuffer1.charAt(i);
if (s2.indexOf(c) != -1) {
commonChars = commonChars + c;
}
}
I think you should consider that each character can appear multiple times in each String. A typical solution is to do that in two steps:
1) Build a map char -> number of occurrences for each of the two strings
2) Compare the maps to find common characters
Map<Character, Integer> charCount(String s) {
Map<Character, Integer> mapCount = new HashMap<>();
for (char c : s.toCharArray()) {
if (!s.contains(c)) mapCount.put(c, 1 + mapCount.computeIfAbsent(c, e -> 0));
}
}
void findCommonCharacter(String s1, String s2) {
Map<Character, Integer> m1 = charCount(s1);
Map<Character, Integer> m2 = charCount(s2);
for (char c : m1.keys()) {
int occurrences = m2.containsKey(c) ? Math.min(m1.get(m1), m2.get(m2) : 0;
if (occurrences>0) {
System.out.println("The two strings share " + occurrences
+ " occurrences of " + c);
}
}
}
Here is a method using arrays, we'll fill arrays of a length of 26 and get back to them to know how many of each letter there are in each word and compare both to take the minimum value.
Solution
public static void main(String[] args) {
StringBuffer sb1 = new StringBuffer("dad");
StringBuffer sb2 = new StringBuffer("daddy");
System.out.println(findCommons(sb1, sb2)); // Prints "add"
sb1 = new StringBuffer("Encapsulation");
sb2 = new StringBuffer("Programmation");
System.out.println(findCommons(sb1, sb2)); // Prints "aainopt"
}
public static StringBuffer findCommons(StringBuffer b1, StringBuffer b2){
int[] array1 = new int[26];
int[] array2 = new int[26];
int[] common = new int[26];
StringBuffer sb = new StringBuffer("");
for (int i = 0 ; i < (b1.length() < b2.length() ? b2:b1).length() ; i++){
if (i < b1.length()) array1[Character.toLowerCase(b1.charAt(i)) - 'a'] += 1;
if (i < b2.length()) array2[Character.toLowerCase(b2.charAt(i)) - 'a'] += 1;
}
for (int i = 0 ; i < 26 ; i++){
common[i] = array1[i] < array2[i] ? array1[i] : + array2[i];
for (int j = 0 ; j < common[i] ; j++) sb.append((char)('a' + i));
}
return sb;
}
Use a Set intersection - you only need 3 lines of code:
Set<Character> common = stringBuffer1.toString().chars()
.mapToObj(c -> Character.valueOf((char)c)).collect(Collectors.toSet());
common.retainAll(stringBuffer2.toString().chars()
.mapToObj(c -> Character.valueOf((char)c)).collect(Collectors.toSet()));
StringBuffer result = new StringBuffer(common.stream().map(String::valueOf).collect(Collectors.joining("")));
But you shouldn't be using StringBuffer for this; just working with Strings will do it more easily:
public static String commonCharacters(String s1, String s2) {
Set<String> chars = Arrays.stream(s1.split("")).collect(Collectors.toSet());
chars.retainAll(Arrays.stream(s2.split("")).collect(Collectors.toSet()));
return chars.stream().reduce((a, b) -> a + b).get();
}
Related
I tried to code a program to detect an anagram with 2 Strings given.
My approach is to convert both strings to char Arrays and then sort them before comparing them.
I know I could use the sort() function but I don't want to use any imports for training purposes.
The problem is i want my programm to ignore blanks while scanning for an anagram.
in the current version the ouput is like this:
(triangle, relating) ---> true
(tri angle, relating) ---> false
while it should be both true.
i would be thankful for any help!
heres my code, (please ignore my comments):
public static boolean anagramCheck(String a, String b) {
boolean r = true;
// In Char Arrays umwandeln /
char[] Ca = a.toCharArray();
char[] Cb = b.toCharArray();
// Laengen Abfrage
int L1 = Ca.length;
int L2 = Cb.length;
// Erste For-Schleife
for (int i = 0; i < L1; i++) {
for (int j = i + 1; j < L1; j++) {
if (Ca[j] < Ca[i]) {
char temp = Ca[i];
Ca[i] = Ca[j];
Ca[j] = temp;
}
}
}
// Zweite For-schleife
for (int i = 0; i < L2; i++) {
for (int j = i + 1; j < L2; j++) {
if (Cb[j] < Cb[i]) {
char temp = Cb[i];
Cb[i] = Cb[j];
Cb[j] = temp;
}
}
}
// Char Arrays zu Strings
String S1 = String.valueOf(Ca);
String S2 = String.valueOf(Cb);
// Vergleich und Ausgabe
if (S1.compareTo(S2) == 0) {
return r;
}
else {
r = false;
return r;
}
}
}
The String.replace(String, String) is the non-regexp replace method.
So remove all spaces:
String S1 = String.valueOf(Ca).replace(" ", "");
String S2 = String.valueOf(Cb).replace(" ", "");
It would be nicer to do this on a and b.
public static boolean isAnagram(String one, String two) {
char[] letters = new char[26];
for (int i = 0; i < one.length(); i++) {
char ch = Character.toLowerCase(one.charAt(i));
if (Character.isLetter(ch))
letters[ch - 'a']++;
}
for (int i = 0; i < two.length(); i++) {
char ch = Character.toLowerCase(two.charAt(i));
if (Character.isLetter(ch))
letters[ch - 'a']--;
}
for (int i = 0; i < letters.length; i++)
if (letters[i] != 0)
return false;
return true;
}
Generally, less code is better (if it’s readable). And learning a language means learning the built in libraries.
Here’s a method to return a String of sorted chars:
public static String sortChars(String str) {
return str.replace(" ", "").chars().sorted()
.mapToObj(c -> (char)c + "")
.collect(Collectors.joining(""));
}
With this method, your main method becomes:
public static boolean anagramCheck(String a, String b) {
return sortedChars(a).equals(sortedChars(b));
}
Refactoring like this, using well-named methods makes your code easier to understand, test, debug and maintain.
It’s worth noting that you don’t actually need a sorted String… a sorted array would serve equally well, and requires less code:
public static int[] sortChars(String str) {
return str.replace(" ", "").chars().sorted().toArray();
}
public static boolean anagramCheck(String a, String b) {
return Arrays.equal(sortedChars(a), sortedChars(b));
}
A frequency map could be created with the characters from String one incrementing and the characters from String two decrementing, then the resulting map should contain only 0 as values.
To skip non-letters, Character::isLetter can be used.
public static boolean isAnagram(String a, String b) {
Map<Character, Integer> frequencies = new HashMap<>();
for (int i = 0, na = a.length(), nb = b.length(), n = Math.max(na, nb); i < n; i++) {
if (i < na && Character.isLetter(a.charAt(i)))
frequencies.merge(Character.toLowerCase(a.charAt(i)), 1, Integer::sum);
if (i < nb && Character.isLetter(b.charAt(i)))
frequencies.merge(Character.toLowerCase(b.charAt(i)), -1, Integer::sum);
}
return frequencies.values().stream().allMatch(x -> x == 0);
}
public static String frequencySort(String s) {
String answer = "";
HashMap<Character, Integer> map = new HashMap<>();
for(int i = 0; i < s.length(); i++) {
map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1);
}
//System.out.println(map.get('l'));
//System.out.println(map.get('e'));
PriorityQueue<Character> q = new PriorityQueue<>(new Comparator<Character>() {
#Override
public int compare(Character o1, Character o2) {
if(map.get(o1) == map.get(o2)) {
//System.out.println("o1 - o2: " + o1 + o2 + " " + (o1 - o2));
return o2 - o1;
}
else {
return map.get(o2) - map.get(o1);
}
}
});
for(int i = 0; i < s.length(); i++) {
q.add(s.charAt(i));
}
while(!q.isEmpty()) {
answer += String.valueOf(q.poll());
}
return answer;
}
My code looks like this, but I don't know why sometimes when the frequency got same but the queue didn't poll out the char as I write in the comparator, for example it output as ccwccwwwcwwwwwwccwwcccwcwwcw
Looks pretty good! Not sure about your bug though, this'll just pass in Java without using PriorityQueue:
public final class Solution {
public static final String frequencySort(
String s
) {
Map<Character, Integer> countmap = new HashMap<>();
for (char character : s.toCharArray()) {
countmap.put(character, 1 + countmap.getOrDefault(character, 0));
}
List<Character>[] bucket = new List[s.length() + 1];
for (char key : countmap.keySet()) {
int frequency = countmap.get(key);
if (bucket[frequency] == null) {
bucket[frequency] = new ArrayList<>();
}
bucket[frequency].add(key);
}
StringBuilder sb = new StringBuilder();
for (int pos = bucket.length - 1; pos >= 0; pos--)
if (bucket[pos] != null)
for (char character : bucket[pos])
for (int i = 0; i < pos; i++) {
sb.append(character);
}
return sb.toString();
}
}
Here are some of LeetCode's official solutions with comments:
public String frequencySort(String s) {
if (s == null || s.isEmpty()) return s;
// Create a sorted Array of chars.
char[] chars = s.toCharArray();
Arrays.sort(chars);
// Convert identical chars into single Strings.
List<String> charStrings = new ArrayList<String>();
StringBuilder currentString = new StringBuilder();
currentString.append(chars[0]);
for (int i = 1; i < chars.length; i++) {
if (chars[i] != chars[i - 1]) {
charStrings.add(currentString.toString());
currentString = new StringBuilder();
}
currentString.append(chars[i]);
}
charStrings.add(currentString.toString());
// Our comparator is (a, b) -> b.length() - a.length().
// If a is longer than b, then a negative number will be returned
// telling the sort algorithm to place a first. Otherwise, a positive
// number will be returned, telling it to place a second.
// This results in a longest to shortest sorted list of the strings.
Collections.sort(charStrings, (a, b) -> b.length() - a.length());
// Use StringBuilder to build the String to return.
StringBuilder sb = new StringBuilder();
for (String str : charStrings) sb.append(str);
return sb.toString();
}
public String frequencySort(String s) {
// Count up the occurances.
Map<Character, Integer> counts = new HashMap<>();
for (char c : s.toCharArray()) {
counts.put(c, counts.getOrDefault(c, 0) + 1);
}
// Make a list of the keys, sorted by frequency.
List<Character> characters = new ArrayList<>(counts.keySet());
Collections.sort(characters, (a, b) -> counts.get(b) - counts.get(a));
// Convert the counts into a string with a sb.
StringBuilder sb = new StringBuilder();
for (char c : characters) {
int copies = counts.get(c);
for (int i = 0; i < copies; i++) {
sb.append(c);
}
}
return sb.toString();
}
public String frequencySort(String s) {
if (s == null || s.isEmpty()) return s;
// Count up the occurances.
Map<Character, Integer> counts = new HashMap<>();
for (char c : s.toCharArray()) {
counts.put(c, counts.getOrDefault(c, 0) + 1);
}
int maximumFrequency = Collections.max(counts.values());
// Make the list of buckets and apply bucket sort.
List<List<Character>> buckets = new ArrayList<>();
for (int i = 0; i <= maximumFrequency; i++) {
buckets.add(new ArrayList<Character>());
}
for (Character key : counts.keySet()) {
int freq = counts.get(key);
buckets.get(freq).add(key);
}
// Build up the string.
StringBuilder sb = new StringBuilder();
for (int i = buckets.size() - 1; i >= 1; i--) {
for (Character c : buckets.get(i)) {
for (int j = 0; j < i; j++) {
sb.append(c);
}
}
}
return sb.toString();
}
This solution uses Priority Queue similar to yours (was in a comment on this link):
class Solution {
public String frequencySort(String s) {
PriorityQueue<Map.Entry<Character, Integer>> maxHeap =
new PriorityQueue<>((a, b) -> b.getValue() - a.getValue());
Map<Character, Integer> frequency = new HashMap<>();
for (Character c : s.toCharArray()) {
frequency.put(c, frequency.getOrDefault(c, 0) + 1);
}
maxHeap.addAll(frequency.entrySet());
StringBuilder sb = new StringBuilder();
while (maxHeap.size() > 0) {
Map.Entry<Character, Integer> entry = maxHeap.remove();
for (int i = 0; i < entry.getValue(); i++) {
sb.append(entry.getKey());
}
}
return sb.toString();
}
}
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.
I am using HashMap() for the problem but am facing issues with regards to order and occurrence of characters in output.
I tried to reverse the String builder both while iteration and after StringBuilder was created, still face another issues.
int l1 = inputStr1.length();
int l2 = inputStr2.length();
StringBuilder mkr = new StringBuilder();
HashMap<Character, Integer> res = new HashMap<>();
for (int i = 0; i < l1; i++) {
res.put(inputStr1.charAt(i),i);
}
for (int j = 0; j < l2; j++) {
if (res.containsKey(inputStr2.charAt(j))){
mkr.append(inputStr2.charAt(j));
}
}
mkr = mkr.reverse(); // Code only used in Test Scenario - 2
String result = mkr.toString();
if(result == null){return null;}
return result;
Test Scenario 1 -
Input String 1 : Hello
Input String 2 : world
Expected output is: lo
Actual output generated by my code: ol
Test Scenario 2 - [After reversing the StringBuilder]
Input String 1: hi you are good
Input String 2 : hi man
Expected output is: hi a
Actual output generated by my code: a ih
Your approach seems to be correct. But since you are expecting the final intersection characters to be in order with input String 1, instead of creating a hashmap of characters for string 1, and iterating over string 2, if those operations are reversed, it would give you the expected answer. But this may return duplicates. For e.g, if inputString1 = 'apple' and inputString2 = 'pot', this method may return two p's instead of 1. To avoid this problem, you can remove the character from hashmap once it matches with character in inputString1.
int l1 = inputStr1.length();
int l2 = inputStr2.length();
StringBuilder mkr = new StringBuilder();
HashMap<Character, Integer> res = new HashMap<>();
for (int i = 0; i < l2; i++) {
res.put(inputStr2.charAt(i),i);
}
for (int j = 0; j < l1; j++) {
if (res.containsKey(inputStr1.charAt(j))){
res.remove(inputStr1.charAt(j));
mkr.append(inputStr1.charAt(j);
}
}
String result = mkr.toString();
if(result == null){return null;}
return result;
Finally, found the solution with help of #askipop :
if(inputStr1 == null || inputStr2 == null){
return null;
}
String res = "";
HashMap<Character, Integer> hm = new HashMap<>();
for(int i = 0; i < inputStr2.length(); i++){
Character c = inputStr2.charAt(i);
if(hm.containsKey(c)){
hm.put(c, hm.get(c) + 1);
} else{
hm.put(c, 1);
}
}
for(int i = 0; i < inputStr1.length(); i++){
Character ch = inputStr1.charAt(i);
if(hm.containsKey(ch)){
res += ch;
int c = hm.get(ch);
if (c - 1 > 0){
hm.put(ch, c - 1);
} else{
hm.remove(ch);
}
}
}
if(res.length() > 0){
return res;
}
return null;
}
I am trying to solve this question: https://www.hackerrank.com/challenges/anagram
Here's my code:
import java.util.*;
public class Anagram {
public static void main(String[] args)
{
Scanner reader = new Scanner(System.in);
int t = reader.nextInt();
while((t--) > 0)
{
String input = reader.nextLine();
if((input.length()) % 2 == 1)
System.out.println(-1);
else
{
int x = input.length();
int q = (int)(Math.floor((x / 2)));
String input1 = input.substring(0, q);
String input2 = input.substring(q, x);
int [] count2 = new int[26];
for(int i = 0; i < input2.length(); i++)
{
char ch2 = input2.charAt(i);
count2[ch2 - 'a']++;
}
// int [] count1 = new int[26];
for(int i = 0; i < input1.length(); i++)
{
char ch1 = input1.charAt(i);
if(count2[i] > 0)
count2[ch1 - 'a']--;
}
int count = 0;
for(int j = 0; j < 26; j++)
{
count = count + Math.abs(count2[j]);
}
System.out.println(count);
}
}
}
}
Sample Input
6
aaabbb
ab
abc
mnop
xyyx
xaxbbbxx
Expected Output
3
1
-1
2
0
1
My output
0
4
1
-1
2
2
Can anyone please tell me where it went wrong? I couldn't find the error...
Your first output always comes 0, because of this line:
int t = reader.nextInt();
followed by reader.nextLine();. Check this post for more details on that. For quick fix, change that line to:
int t = Integer.parseInt(reader.nextLine());
Now, let's start with the below two statements:
int x = input.length();
int q = (int)(Math.floor((x/2)));
No need to do a Math.floor there. x/2 is an integer division, and will give you integer result only.
Moving to the 2nd for loop. You used the following condition:
if(count2[i]>0)
count2[ch1-'a']--;
Notice the mistake there in condition? It should be count2[ch1 - 'a'] > 0. And also, you will miss the case where that count is not greater than 0, in which case you would have to do a ++. BTW, since you're anyways doing a Math.abs(), you don't need the condition. Just do a --:
for( int i = 0; i < input1.length(); i++ ) {
char ch1 = input1.charAt(i);
count2[ch1-'a']--;
}
BTW, the final result would be count / 2, and not count, because count contains the total mismatch from input1 to input2 and vice-versa. But we just have to fix one of them to match the other. So, just consider half the total mismatch.
You can use this to check if two strings are palindromes:
String original = "something";
String reverse = new StringBuilder(original).reverse().toString();
boolean anagram = original.equals(reverse);
As per your question, main logic could be changed to something like below.
Note - I have added only the main logic and excluded the user inputs here.
public static void main(String[] args) {
String str = "acbacccbaac";
int len = str.length();
String str1 = null, str2 = null;
if(len %2 != 0) {//check for odd length
str1 = str.substring(0, len/2);
str2 = str.substring(len/2+1, len);
}else {//check for even length
str1 = str.substring(0, len/2);
str2 = str.substring(len/2, len);
}
char[] arr1 = str1.toLowerCase().toCharArray();
Arrays.sort(arr1);
char[] arr2 = str2.toLowerCase().toCharArray();
Arrays.sort(arr2);
if(Arrays.equals(arr1, arr2))
System.out.println("Yes");
else
System.out.println("No");
}
I implemented this way for the same problem in my HackerRank profile, and works great.
This is my solution to the prob and it works!
static int anagram(String s) {
String a = "";
String b = "";
if (s.length() % 2 == 0) {
a = s.substring(0, s.length() / 2);
b = s.substring((s.length() / 2), s.length());
}
if (s.length() % 2 != 0) {
a = s.substring(0, s.length() / 2);
b = s.substring((s.length() / 2), s.length());
}
if (a.length() == b.length()) {
char[] aArray = a.toCharArray();
char[] bArray = b.toCharArray();
HashMap<Character, Integer> aMap = new HashMap<Character, Integer>();
HashMap<Character, Integer> bMap = new HashMap<Character, Integer>();
for (char c : aArray) { // prepare a Hashmap of <char>,<count> for first string
if (aMap.containsKey(c)) {
aMap.put(c, aMap.get(c) + 1);
} else {
aMap.put(c, 1);
}
}
for (char c : bArray) {// prepare a Hashmap of <char>,<count> for second string
if (bMap.containsKey(c)) {
bMap.put(c, bMap.get(c) + 1);
} else {
bMap.put(c, 1);
}
}
int change = 0;
for (Map.Entry<Character, Integer> entry : bMap.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
if (!aMap.containsKey(entry.getKey())) {
change += entry.getValue();
} else {
if (entry.getValue() > aMap.get(entry.getKey())) {
change += entry.getValue() - aMap.get(entry.getKey());
} else {
//change += entry.getValue();
}
}
}
return change;
} else {
return -1;
}
}
Need a Java function to find intersection of two strings. i.e. characters common to the strings.
Example:
String s1 = new String("Sychelless");
String s2 = new String("Sydney");
Using HashSet<Character>:
HashSet<Character> h1 = new HashSet<Character>(), h2 = new HashSet<Character>();
for(int i = 0; i < s1.length(); i++)
{
h1.add(s1.charAt(i));
}
for(int i = 0; i < s2.length(); i++)
{
h2.add(s2.charAt(i));
}
h1.retainAll(h2);
Character[] res = h1.toArray(new Character[0]);
This is O(m + n), which is asymptotically optimal.
Extract the characters
String.toCharArray
Put them in a Set
Find the intersection
Set.retainAll
Most basic approach:
String wordA = "Sychelless";
String wordB = "Sydney";
String common = "";
for(int i=0;i<wordA.length();i++){
for(int j=0;j<wordB.length();j++){
if(wordA.charAt(i)==wordB.charAt(j)){
common += wordA.charAt(i)+" ";
break;
}
}
}
System.out.println("common is: "+common);
More detail on saugata's response (appeared while I was writing this): -
public static void main(String[] args) {
String s1 = "Seychelles";
String s2 = "Sydney";
Set<Character> ss1 = toSet(s1);
ss1.retainAll(toSet(s2));
System.out.println(ss1);
}
public static Set<Character> toSet(String s) {
Set<Character> ss = new HashSet<Character>(s.length());
for (char c : s.toCharArray())
ss.add(Character.valueOf(c));
return ss;
}
I think the algorithm you are looking for is the problem of the longest common subsequence
Found same question here, refer this
Implementing an efficent algorithm to find the intersection of two strings
By means of Guava this task seems much easier:
String s1 = new String("Sychelless");
String s2 = new String("Sydney");
Set<String> setA = Sets.newHashSet(Splitter.fixedLength(1).split(s1));
Set<String> setB = Sets.newHashSet(Splitter.fixedLength(1).split(s2));
Sets.intersection(setA, setB);
Optimized solution:
public static String twoStrings(String s1, String s2){
HashSet<Character> stringOne = new HashSet<Character>(), stringTwo = new HashSet<Character>();
int stringOneLength = s1.length();
int stringTwoLength = s2.length();
for(int i=0; i<stringOneLength || i<stringTwoLength; i++) {
if(i < stringOneLength)
stringOne.add(s1.charAt(i));
if(i < stringTwoLength)
stringTwo.add(s2.charAt(i));
}
stringOne.retainAll(stringTwo);
return stringOne.toString();
}
I have used TreeSet. And retainAll() in TreeSet to get matched elements.
Oracle Doc:
retainAll(Collection<?> c)
Retains only the elements in this set that are contained in the
specified collection (optional operation).
String s1 = new String("Sychelless");
String s2 = new String("Sydney");
Set<Character> firstSet = new TreeSet<Character>();
for(int i = 0; i < s1.length(); i++) {
firstSet.add(s1.charAt(i));
}
Set<Character> anotherSet = new TreeSet<Character>();
for(int i = 0; i < s2.length(); i++) {
anotherSet.add(s2.charAt(i));
}
firstSet.retainAll(anotherSet);
System.out.println("Matched characters are " + firstSet.toString());//print common strings
//output > Matched characters are [S, e, y]
s1.contains(s2) returns true;
s1.indexOf(s2) returns 0.
s1.indexOf("foo") returns -1
For more sophisticated cases use class Pattern.