Why is StringBuilder.reverse faster than appending to linkedlists - java

Leetcode problem 125. Valid Palindrome:
Given a string s, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.
Example 1:
Input: s = "A man, a plan, a canal: Panama"
Output: true
Explanation: "amanaplanacanalpanama" is a palindrome.
I appended each character into two linked lists, one forwards and one backwards, and compared them. However, I did not pass the time limit. The Leetcode's solution used a StringBuilder and reversed it. I heard that StringBuilder is implemented similar to a linked list. I have no idea why my code is much slower than the solution. I would appreciate any feedback or insights on this topic. Thank you in advance.
My code:
class Solution {
public boolean isPalindrome(String s) {
LinkedList<Character> forward = new LinkedList<Character>();
LinkedList<Character> backward = new LinkedList<Character>();
for(int i = 0 ; i < s.length() ; i++){
char ch = s.charAt(i);
if(Character.isLetterOrDigit(ch)){
if(Character.isLetter(ch)) ch = Character.toLowerCase(ch);
forward.addLast(ch);
backward.addFirst(ch);
}
}
for(int i = 0 ; i < forward.size() ; i++){
if(forward.get(i) != backward.get(i)) return false;
}
return true;
}
}
Leetcode Solution:
class Solution {
public boolean isPalindrome(String s) {
StringBuilder builder = new StringBuilder();
for (char ch : s.toCharArray()) {
if (Character.isLetterOrDigit(ch)) {
builder.append(Character.toLowerCase(ch));
}
}
String filteredString = builder.toString();
String reversedString = builder.reverse().toString();
return filteredString.equals(reversedString);
}
}

If you look at how reverse() method is implemented in AbstractStringBuilder you can see that it uses array to store characters. It is the main difference between StringBuilder and your solution. forward.get(i) and backward.get(i) have O(n) complexity, when value[j] has O(1).
Java 8 implementation:
public AbstractStringBuilder reverse() {
boolean hasSurrogates = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; j--) {
int k = n - j;
char cj = value[j];
char ck = value[k];
value[j] = ck;
value[k] = cj;
if (Character.isSurrogate(cj) ||
Character.isSurrogate(ck)) {
hasSurrogates = true;
}
}
if (hasSurrogates) {
reverseAllValidSurrogatePairs();
}
return this;
}

Actually, Leetcode solution does not seem to be the best and StringBuilder::reverse method does not have to be used at all to detect a palindrome, because it is possible to check the characters from the start and end of the string to its center and as soon as unmatching pair is found the string is NOT a palindrome.
Also conversion of StringBuilder to direct and reversed strings is redundant.
class Solution {
public boolean isPalindrome(String s) {
StringBuilder builder = new StringBuilder(s.length());
for (char ch : s.toCharArray()) {
if (Character.isLetterOrDigit(ch)) {
builder.append(Character.toLowerCase(ch));
}
}
for (int i = 0, n = builder.length(), m = n / 2; i < m; i++) {
if (builder.charAt(i) != builder.charAt(n - i - 1)) {
return false;
}
}
return true;
}
}
Similar solution using Stream API and a regexp to clean out non-letter and non-digit characters (in Unicode):
public static boolean isPalindrome(String s) {
String str = s.replaceAll("[^\\p{L}\\p{N}]", "").toLowerCase();
final int n = str.length();
return IntStream.range(0, n / 2)
.allMatch(i -> str.charAt(i) == str.charAt(n - 1 - i));
}

Related

How to ignore spaces while Sorting a string in java?

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);
}

How do I replace one character occurrence in a string?

How would I replace a string like "Hello" with "Helko", only replacing the second L but not the first?
Use replaceAll with regular expression:
System.out.println("Hello".replaceAll("((?!^).*?|[^l]*l.*?)l","$1k"));
Simple Approach (based on excluding the second l):
Search for first index of l using indexOf and do another search for l but this time start searching from firstL + 1 which will lead to the second index l if exist!
Do a test if there is second l, If So Exclude the second l by using substring which take only the first part (start from zero till secondL) and second part (start from secondL+1 till the end), Concatenate them with k.
public static String removeSecondL(String str) {
int firstL = str.indexOf('l');
int secondL = str.indexOf('l', firstL+1);
if(secondL != -1) {
String firstPart = str.substring(0, secondL);
String secondPart = str.substring(secondL + 1);
return firstPart + 'k' + secondPart;
}
return str;
}
Tests:
public static void main(String[] args) {
System.out.println(removeSecondL("Hello")); // Helko
System.out.println(removeSecondL("lololo")); // lokolo
System.out.println(removeSecondL("no l")); // no l
}
Another Approach: Convert the String into a char array, and declare a variable lFound gonna look for the first occurrence of letter l, if it found next l will be converted to k and exit the loop by break.
String str = "Hello";
char[] chars = str.toCharArray();
boolean lFound = false;
for (int i = 0; i < chars.length; i++) {
if(lFound) {
if(chars[i] == 'l')
{
chars[i] = 'K';
break;
}
}else {
lFound = chars[i] == 'l'; //if(chars[i] == 'l') lFound = true;
}
}
System.out.println(chars); //HelKo
Lately, I turned to the conversation and I saw you writing:
I wanted to replace one instance of a character with another, like "aaaaa" with "aabaa", where only the third 'a' is replaced with 'b', but not the others.
Just follow second approach I posted with additional tests.
public static char[] removeSpecificChar(String str, char a, char b, int position) {
char[] chars = str.toCharArray();
int pos = 1;
for (int i = 0; i < chars.length; i++) {
if(pos == position) {
if(chars[i] == a)
{
chars[i] = b;
break;
}
}else {
pos += (chars[i] == a) ? 1 : 0;
}
}
return chars;
}
Test:
String str = "aaaaa";
System.out.println(removeSpecificChar(str, 'a', 'b', 3));
Print:
aabaa

How to reverse capitalization of specific alphabetical characters in a string (case insensitive), leaving all other chars in the string unaffected?

I am trying to implement a function that searches a given string and reverses capitalization of all occurrences of particular alphabetical characters (case insensitive), leaving numbers, special characters, and other alphabetical characters unaffected.
For example, if theString = "abc123XYZ" and reverseCaps= "cyz", the result should be "abC123Xyz".
I have tried various implementations and fixes, but cannot get it to work properly. The result I am getting now is "ABC123xyz".
Here is my code:
public static String flipCaseChars(String theString, String reverseCap) {
StringBuilder buf = new StringBuilder(theString.length());
for (int i = 0; i < theString.length(); i++) {
char c = theString.charAt(i);
if (Character.isUpperCase(c)) {
buf.append(Character.toLowerCase(c));
}
else if (Character.isLowerCase(c)) {
buf.append(Character.toUpperCase(c));
}
// if char is neither upper nor lower
else {
buf.append(c);
}
}
return buf.toString();
}
What should I do? Any help would be very much appreciated.
public static String flipCaseChars(String theString, String reverseCap) {
StringBuilder buf = new StringBuilder(theString.length());
for (int i = 0; i < theString.length(); i++) {
char c = theString.charAt(i);
if (reverseCap.indexOf(c) >= 0){
if (Character.isUpperCase(c)) {
buf.append(Character.toLowerCase(c));
} else if (Character.isLowerCase(c)) {
buf.append(Character.toUpperCase(c));
} else {
buf.append(c);
}
} else {
buf.append(c);
}
}
return buf.toString();
}
Am I missing something here? I see that some answers here are getting voted up but they're not doing what the OP has requested. According to the example input ("abc123XYZ") and output ("abC123Xyz"), I see it that the letter-case of the characters within the reverseCaps string variable is irrelevant. They could have been any letter-case but, if any one of them are encountered within the input string (theString) regardless of current letter-case state any one of the supplied characters letter-case if flipped to its' opposing state.
So if the Input String was: ab-c-123-C-XYz and the reverseCaps variable contained "cyz" then the output should be: ab-C-123-c-XyZ. Am I mistaken?
If I'm not mistaken then the following code will carry out the task explained above:
public static String flipCaseCharacters(String inputString, String charactersToFlip) {
StringBuilder newString = new StringBuilder();
for (char inChar : inputString.toCharArray()) {
for (char ctFlip : charactersToFlip.toCharArray()) {
if (Character.toUpperCase(inChar) == Character.toUpperCase(ctFlip)) {
inChar = Character.isUpperCase(inChar) ?
Character.toLowerCase(inChar) :
Character.toUpperCase(inChar);
break;
}
}
newString.append(inChar);
}
return newString.toString();
}
An easier way is to loop through the reverseCap String and do a conditional replace
for (char c : reverseCap.toCharArray()) {
if (Character.isLowerCase(c)) {
theString = theString.replace(c, Character.toUpperCase(c));
}
else {
theString = theString.replace(c, Character.toLowerCase(c));
}
}
return theString;
No need to check if the character is upper or lower case. It just flips case of the character as appropriate. This presumes that the reverse list of characters is all lowercase, as shown in the example.
It works by checking and then manipulating the bit 0x20that determines upper and lower case in ASCII characters.
The ^ is the exclusive OR operator that flips to the opposite case by flipping the case bit.
public static String flipCaseChars(String theString, String reverseCap) {
StringBuilder sb = new StringBuilder();
for (char c : theString.toCharArray()) {
// is the character in the list?
if (reverseCap.indexOf(c | 0x20) >= 0) {
c ^= 0x20; // flip the case
}
sb.append(c);
}
return sb.toString();
}
I basically made a HashSet<Character> of reverseCap and then the rest follows #toootooo's answer
Here it is:
static String flipCaseChars(String theString, String reverseCap) {
final StringBuilder stringBuilder = new StringBuilder(theString.length());
HashSet<Character> collect = new HashSet<>();
for (int i = 0; i < reverseCap.length(); i++) {
collect.add(Character.toLowerCase(reverseCap.charAt(i)));
collect.add(Character.toUpperCase(reverseCap.charAt(i)));
}
for (int i = 0; i < theString.length(); i++) {
char currentChar = theString.charAt(i);
if (collect.contains(currentChar)) {
if (Character.isUpperCase(currentChar)) {
currentChar = Character.toLowerCase(currentChar);
} else if (Character.isLowerCase(currentChar)){
currentChar = Character.toUpperCase(currentChar);
}
}
stringBuilder.append(currentChar);
}
return stringBuilder.toString();
}
The only advantage of this approach is that the lookup of the characters in reverseCap is done in constant time and the time complexity is directly propertional to the length of theString.

The fastest method of determining if a string is a palindrome

I need an algorithm that verify with the fastest possible execution time, if a string is a palindrome ( the string can be a proposition with uppercase or lowercase letter, spaces etc.). All of this in Java. I got a sample :
bool isPalindrome(string s) {
int n = s.length();
s = s.toLowerCase();
for (int i = 0; i < (n / 2) + 1; ++i) {
if (s.charAt(i) != s.charAt(n - i - 1)) {
return false;
}
}
return true;
}
I transformed the string in lowercase letter using .toLowerCase() function, but I don't know how much it affects the execution time .
And as well I don't know how to solve the problem with punctuation and spaces between words in a effective way.
I think you can just check for string reverse, not?
StringBuilder sb = new StringBuilder(str);
return str.equals(sb.reverse().toString());
Or, for versions earlier than JDK 1.5:
StringBuffer sb = new StringBuffer(str);
return str.equals(sb.reverse().toString());
This avoids any copying. The functions isBlank and toLowerCase are rather unspecified in your question, so define them the way you want. Just an example:
boolean isBlank(char c) {
return c == ' ' || c == ',';
}
char toLowerCase(char c) {
return Character.toLowerCase(c);
}
Don't worry about the costs of method calls, that's what the JVM excels at.
for (int i = 0, j = s.length() - 1; i < j; ++i, --j) {
while (isBlank(s.charAt(i))) {
i++;
if (i >= j) return true;
}
while (isBlank(s.charAt(j))) {
j--;
if (i >= j) return true;
}
if (toLowerCase(s.charAt(i)) != toLowerCase(s.charAt(j))) return false;
}
return true;
Try to benchmark this... I'm hoping mu solution could be the fastest, but without measuring you never know.
Your solution seems just fine when it comes to effectiveness.
As for your second problem, you can just remove all spaces and dots etc before you start testing:
String stripped = s.toLowerCase().replaceAll("[\\s.,]", "");
int n = stripped.length();
for (int i = 0; i < (n / 2) + 1; ++i) {
if (stripped.charAt(i) != stripped.charAt(n - i - 1)) {
...
Effective is not the same of efficient.
Your answer is effective as long you consider spaces, special characters and so on. Even accents could be problematic.
About efficiency, toLowerCase is O(n) and any regexp parsing will be O(n) also. If you are concerning about that, convert and compare char by char should be the best option.
Here is my try:
public static boolean isPalindrome(String s)
{
int index1 = 0;
int index2 = s.length() -1;
while (index1 < index2)
{
if(s.charAt(index1) != s.charAt(index2))
{
return false;
}
index1 ++;
index2 --;
}
return true;
}
Here's some insight to my way of detecting a palindrome using Java. Feel free to ask question :) Hope I could help in some way....
import java.util.Scanner;
public class Palindrome {
public static void main(String[]args){
if(isReverse()){System.out.println("This is a palindrome.");}
else{System.out.print("This is not a palindrome");}
}
public static boolean isReverse(){
Scanner keyboard = new Scanner(System.in);
System.out.print("Please type something: ");
String line = ((keyboard.nextLine()).toLowerCase()).replaceAll("\\W","");
return (line.equals(new StringBuffer(line).reverse().toString()));
}
}
In normal cases :
StringBuilder sb = new StringBuilder(myString);
String newString=sb.reverse().toString();
return myString.equalsIgnoreCase(newString);
In case of case sensitive use :
StringBuilder sb = new StringBuilder(myString);
String newString=sb.reverse().toString();
return myString.equals(newString);

Checking if two strings are permutations of each other

How to determine if two strings are permutations 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;
}
Sort the 2 strings by characters and compare if they're the same (O(n log n) time, O(n) space), or
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:
Figure out what language this is
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

Categories

Resources