complexity of printing string permutations - java

Here is my code to print string permutations. I am struggling to calculate time complexity of the functions. Can someone please suggest few pointers. And if there is any more time efficient method ?
import java.util.ArrayList;
public class Permutations {
public static void main(String[] args){
ArrayList<String> aList = permutation("ABCC");
for(int i=0; i<aList.size(); i++){
System.out.print(aList.get(i) + " ");
}
}
public static ArrayList<String> permutation(String s) {
// The result
ArrayList<String> res = new ArrayList<String>();
// If input string's length is 1, return {s}
if (s.length() == 1) {
res.add(s);
} else if (s.length() > 1) {
int lastIndex = s.length() - 1;
// Find out the last character
String last = s.substring(lastIndex);
// Rest of the string
String rest = s.substring(0, lastIndex);
// Perform permutation on the rest string and
// merge with the last character
res = merge(permutation(rest), last);
}
return res;
}
public static ArrayList<String> merge(ArrayList<String> list, String c) {
ArrayList<String> res = new ArrayList<String>();
// Loop through all the string in the list
for (String s : list) {
// For each string, insert the last character to all possible postions
// and add them to the new list
for (int i = 0; i <= s.length(); ++i) {
String ps = new StringBuffer(s).insert(i, c).toString();
res.add(ps);
}
}
return res;
}
}

For speed improvements, a LinkedList would be faster, also using the same StringBuffer and StringBuffer#setCharAt(int, char). Something like this maybe:
List<String> permutations = new ArrayList<String>(initial size); // initial size to avoid multiple arrays to be created
if (s.length() == 1) {
permutations.add(s);
} else {
StringBuffer sb = new StringBuffer(s);
loop { // some kind of loop
sb.setCharAt(0, 'a'); // do the next permutation
permutations.add(sb.toString());
}
}
return permutations;

Plain merge() is O(n^2). With recurrence it seems to be O(n^3)

Related

Getting distinct subsequences of a string equal to a particular string via recursion

Code logic
In order to find all subsequences of String s that are equal to String t, I made a recursive method getSub() to get all the subsequences of input string s and added it to the list. Now I loop through the list (in numDistinct method) and try to see all those subsequences in the list that matches the string t. In case there is a match count should be incremented and later returned.
BUT
Question
In the following code count never increases, which means the if condition (in numDistinct method) does not work as it was intended. Why t which is a string and list.get(i) which should also return a string doesn't seem to work with equals method?
public static int numDistinct(String s, String t) {
int count = 0;
ArrayList<String> list = new ArrayList<>();
getSub(s, list);
StdOut.println(list);
for (int i = 0; i < list.size(); i++) {
if (t.equals(list.get(i))) {
count++;
}
}
return count;
}
private static int getSub(String s, ArrayList<String> list) {
if (s.length() == 0) {
list.add(" ");
return 1;
}
int smallOutput = getSub(s.substring(1), list);
char[] cha = s.toCharArray();
for (int i = 0; i < smallOutput; i++) {
list.add(cha[0] + list.get(i));
}
return 2 * smallOutput;
}
public static void main(String[] args) {
String s = "rabbbit";
String t = "rabbit";
StdOut.println(numDistinct(s, t));
}
The way you are creating the strings in the list ensures there is a space character at the end of each string. As such, none of them are equal to t.
if (s.length() == 0) {
list.add(""); // remove the space from this line
return 1;
}

Storing an ArrayList inside a HashMap

I have the following code. What I want to do is to have an ArrayList filled using the permutation function, keep that Array in a HashMap, and start the process all over again (basically fill the HashMap with ArrayList for each key). I posted the code below however it does not work. I think this is because It's storing the same reference to the list I have declared instead of making a copy of it. I'm a C scrub and a Java newbie so any help is appreciated!
public class Anagrams
{
public static HashMap<String, ArrayList<String>> permutacii = new HashMap<String, ArrayList<String>>();
public static ArrayList<String> tempList = new ArrayList<String>();
private static void permutation(String prefix, String str)
{
int n = str.length();
if (n == 0)
tempList.add(prefix);
else
{
for (int i = 0; i < n; i++)
permutation(prefix + str.charAt(i),
str.substring(0, i) + str.substring(i+1));
}
}
public static void main(String[] args) {
findAll(System.in);
}
public static void findAll(InputStream inputStream)
{
Scanner scanner = new Scanner(inputStream);
while(scanner.hasNextLine())
{
String line = scanner.nextLine();
permutation("", line);
permutacii.put(line, tempList);
tempList.clear();
}
}
}
You only have one List of which you store multiple references in the HashMap. And you clear that List at the end of each iteration.
One possible way to fix your problem :
while(scanner.hasNextLine())
{
String line = scanner.nextLine();
tempList = new ArrayList<String>();
permutation("", line);
permutacii.put(line, tempList);
}
Though I think the code would be more readable if you make tempList a local variable and pass it as an argument to the permutation method :
while(scanner.hasNextLine())
{
String line = scanner.nextLine();
ArrayList<String> tempList = new ArrayList<String>();
permutation("", line, tempList);
permutacii.put(line, tempList);
}
and modify the permutation accordingly :
private static void permutation(String prefix, String str, ArrayList<String> tempList)
{
int n = str.length();
if (n == 0)
tempList.add(prefix);
else
{
for (int i = 0; i < n; i++)
permutation(prefix + str.charAt(i),
str.substring(0, i) + str.substring(i+1),
tempList);
}
}

Arranging word to formpalindrome

Hello i´ve been trying to form palindromes from this input:
String[] text ={"ivcci", "oyotta", "cecarar","bbb","babbbb"};
getPalindrome(text);
and i need to rearrange all words in array to produce this output
civic
-1
rececar
bbb
bbabb
the method expects to receive an array of Strings like
public static String getPalindrome(String[] text){}
"returning -1 means i.g "oyotta" in array can´t form a palíndrome
i´ve been testing this code and it works but i.g "cecarar" is not producing "racecar", as im a bit new in java i used an String intead an array of Strings, can anybody help to write this code properly please?
Thanks a lot!
public static String getPalindrome(String s) {
if (s == null)
return null;
Map<Character, Integer> letters = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (!letters.containsKey(c))
letters.put(c, 1);
else
letters.put(c, letters.get(c) + 1);
}
char[] result = new char[s.length()];
int i = 0, j = result.length - 1;
Character middle = null;
for (Entry<Character, Integer> e : letters.entrySet()) {
int val = e.getValue();
char c = e.getKey();
if (val % 2 != 0) {
if (middle == null && s.length() % 2 != 0) {
middle = c;
val--;
} else
return "-1";
}
for (int k = 0; k < val / 2; k++) {
result[i++] = c;
result[j--] = c;
}
}
if (middle != null)
result[result.length / 2] = middle;
return new String(result);
}
In order for a set of characters to be able to produce a palindrome, only one of the letters can be repeated an odd number of times, so you can first weed that out.
Without writing actual code for you, here is the algorithm I would use:
Create a map of characters to a counter. Possible to do int[] counts = new int[26];
Go through each character in the input string, and increment the count: ++counts[Character.toLower(c)-'a'];
Then go through each character, and see if its odd if (counts[i] & 1 != 0) { if (oddIndex != -1) { return -1; } oddIndex=i; } This will return -1 if there is two or more odd counts.
Then, you can create a StringBuilder, and start with the oddIndex in the middle, if it exists.
Then go through the counts, and add count[i]/2 to the front and back of your string builder.
That'll give you a symmetric string from the original inputs.
Now, if you actually need words, then you'll have to have a dictionary of palindromes. You can actually preprocess all the palindromes to have a map of "sorted character string"=>"palindrome"
class PalindromeChecker
{
final Map<String, String> palindromes = new HashMap<String, String>();
public PalindromeChecker(Iterable<String> allPalindromes) {
for (String palindrome: allPalindromes) {
char[] chars = palindrome.getChars();
Arrays.sort(chars);
palindromes.put(String.valueOf(chars), palindromes);
}
}
public String getPalindrome(String input) {
char[] chars = input.getChars();
Arrays.sort(chars);
return palindromes.get(String.valueOf(chars));
}
}
As other users pointed out, a string can be rearranged as a palindrome only if there is at most one character that appears an odd number of times.
Once you have confirmed that a string can be converted to a palindrome, you can construct the palindrome as follows (this is just one of many methods of course):
place at the sides of the string all the pairs of characters that you can get
place at the middle of the string the single character that is left out, in case there is such a character.
Example:
public class Palindromes {
public static void main(String[] args) {
String[] text = {"ivcci", "oyotta", "cecarar","bbb","babbbb"};
for(String str : text){
evaluatePalindrome(str);
}
}
private static void evaluatePalindrome(String str){
PalindromeCandidate pc = new PalindromeCandidate(str);
if(pc.isPalindrome()){
System.out.println(pc.getPalindrome());
} else {
System.out.println("-1");
}
}
}
public class PalindromeCandidate {
private final CharacterCount characterCount;
public PalindromeCandidate(String originalString) {
this.characterCount = new CharacterCount(originalString);
}
public boolean isPalindrome(){
Collection<Integer> counts = characterCount.asMap().values();
int oddCountOccurrences = 0;
for(Integer count : counts){
oddCountOccurrences += (count%2);
}
return (oddCountOccurrences <= 1);
}
public String getPalindrome(){
if(!isPalindrome()){
throw new RuntimeException("Cannot be rearranged as a palindrome.");
}
Map<Character, Integer> counts = characterCount.asMap();
StringBuilder leftSide = new StringBuilder();
StringBuilder middle = new StringBuilder();
for(Character ch : counts.keySet()){
int occurrences = counts.get(ch);
while(occurrences > 1){
leftSide.append(ch);
occurrences -= 2;
}
if(occurrences > 0){
middle.append(ch);
}
}
StringBuilder rightSide = new StringBuilder(leftSide).reverse();
return leftSide.append(middle).append(rightSide).toString();
}
}
/**
* Thin wrapper around a Map<Character, Integer>. Used for counting occurences
* of characters.
*/
public class CharacterCount {
private final Map<Character, Integer> map;
public CharacterCount(String str) {
this.map = new HashMap<>();
for(Character ch : str.toCharArray()){
increment(ch);
}
}
private void increment(Character ch){
this.map.put(ch, getCount(ch) + 1);
}
private Integer getCount(Character ch){
if(map.containsKey(ch)){
return map.get(ch);
} else {
return 0;
}
}
public Map<Character, Integer> asMap(){
return new HashMap<>(map);
}
}

How to return a String Array

My code is supposed to separate a given String and covert the chosen letters into # and separate and concatenate the words with the chosen letter. My problem is with one of the methods (allWordsWith) in my code, it won't allow me to return a String array. (p.s, the codes that run this one are irrelevant, I'm not supposed to edit those).
import java.util.Arrays;
import java.util.Scanner;
public class LipogramAnalyzer {
private String line;
public LipogramAnalyzer(String text){
line = text;
}
public String mark (char letter){
String replaceletters = line.replace(letter, '#');
return replaceletters;
}
public String[] allWordsWith (char letter){
String[] arr = line.split(" ");
for ( String ss : arr) {
String ary[] = {ss};
for(int i = 0; i < ary.length; i++){
int numindex = ss.indexOf(letter);
if (numindex != -1){
String result = ary[i];
return result;
}
}
}
}
}
"It won't allow me to return a String array"
Sure it will. You're attempting to return a String:
String result = ary[i];
return result;
Even though the return type is a string array:
public String[] allWordsWith (char letter){
You need to return an array, as allWordsWith implies you want multiple values.
But the bigger problem is that you are initializing the result array to a single element
String ary[] = {ss};
and thes lengths of arrays can't be changed after initialization. This means that ary.length in this loop
for(int i = 0; i < ary.length; i++){
will always equal one. That is not what you want.
In addition, you are searching the strings in the result array (ary), even though you just created it, meaning it has nothing in it--that is, all the values are null.
If you want a list of all the strings in line that have the letter in it, try
public String[] allWordsWith (char letter){
String[] asAllWordsInLine = line.split(" ");
java.util.ArrayList<String> alsAllWordsWithChar = new java.util.ArrayList<String>();
for ( String ss : asAllWordsInLine) {
if(ss.indexOf(letter) != -1) {
alsAllWordsWithChar.add(ss);
continue; //No need to check any more letters.
}
}
return alsAllWordsWithChar.toArray(new String[alsAllWordsWithChar.size()]);
}
I've changed the array to a list, since you can't know how many strings will have letter in it. A list can change size, an array can't. When no strings match, this returns an empty array, which is preferred over null. (more info)
I've also made the variable names more meaningful, and stopped checking a word after it matches a character (with continue, which short-circuits the current for-loop iteration).
Finally, the function is not returning anything until all strings have been analyzed, meaning after the for-loop completes. In your original code the return is inside the loop, meaning only the first string is returned.
A useful testing function:
public static final void main(String[] igno_red) {
testLine('b', "abc def ghi cba def ghi");
}
private static final void testLine(char c_letter, String s_line) {
System.out.println("Line: \"" + s_line + "\"");
String[] asAllWordsWith = (new LipogramAnalyzer(s_line)).allWordsWith(c_letter);
System.out.println("Words with '" + c_letter + "': " + Arrays.toString(asAllWordsWith));
}
Return the ary not ary[i]
for(int i = 0; i < ary.length; i++){
int numindex = ss.indexOf(letter);
if (numindex != -1){
String result = ary[i];
/*here*/ return ary;
}
You can return an array. Try return ary instead of return ary[i].
Also, your function must return something in all cases. In other words, you have to have a return statement after your for loops.
public String[] allWordsWith (char letter){
String[] arr = line.split(" ");
for ( String ss : arr) {
String ary[] = {ss};
for(int i = 0; i < ary.length; i++){
int numindex = ss.indexOf(letter);
if (numindex != -1){
String result = ary[i];
// or here return ary;
}
}
}
return ary;
}
Return the array
You are trying to return a String instead of String[] (String array)
Change the code:
if (numindex != -1) {
String result = ary[i];
return result;
}
To:
if (numindex != -1) {
return new String[]{ary[i]};
}
// afrer first for loop
return arr;
If you want to return a complete String-Array at once, I have modified your function (not tested):
public String[] allWordsWith (char letter){
String[] arr = line.split(" ");
String[] result = new String[ary.length];
for ( String ss : arr) {
String ary[] = {ss};
for(int i = 0; i < ary.length; i++){
int numindex = ss.indexOf(letter);
if (numindex != -1){
result[i] = ary[i];
}
}
}
return result;
}
As already said, you're returning a String instead of a String[]. Also note that your code will only ever return a single "word". I modified your code to add every word that contains your character and add it to a List. At the end of the loop the List is converted to a String[].
public String[] allWordsWith(char letter)
{
String[] arr = line.split(" ");
List<String> result = new ArrayList<>();
for (String ss : arr) {
int numindex = ss.indexOf(letter);
if (numindex != -1) {
result.add(ss);
}
}
return result.toArray(new String[result.size()]);
}

Interleaving of two strings

I have two strings str1 and str2.
Is there any algorithm that can be used in order to print out all interleavings of the two strings using recursion?
Update:
public class Interleave {
private String resultString[] = new String[10];
private String[] interStr(String str1, String str2){
int n = ((Factorial.factorial(str1.length() + str2.length())) / (Factorial.factorial(str1.length()) * Factorial.factorial(str2.length())));
//n is number of interleavings based on (str1.length()+str2.length())! / (str1.length()! * str2.length()!)
if(str1.length() == 0){
resultString[0] = str2;
return resultString;
}
if(str2.length() == 0){
resultString[0] = str1;
return resultString;
}
else{
for(int i = 0; i < n; i++){
resultString[i]= str1.substring(0, 1) + interStr(str1.substring(1), str2.substring(1));
}
}
return resultString;
}
public static void main(String[] args) {
Interleave obj = new Interleave();
obj.interStr("12", "abc");
for(int i = 0; i < obj.resultString.length; i ++){
System.out.println(obj.resultString[i]);
}
}
}
The question simply asked whether a recursive algorithm exists for the problem, and the answer is yes. To find it, look for the base case and then for the "step".
The base case is when one of the two strings are empty:
interleave(s1, "") = {s1}
interleave("", s2) = {s2}
Notice the order of the arguments doesn't really matter, because
interleave("ab", "12") = {"ab12", "a1b2", "1ab2", "a12b", "1a2b", "12ab"} = interleave("12", "ab")
So since the order doesn't matter we'll look at recursing on the length of the first string.
Okay so let's see how one case leads to the next. I'll just use a concrete example, and you can generalize this to real code.
interleave("", "abc") = {"abc"}
interleave("1", "abc") = {"1abc", "a1bc", "ab1c", "abc1"}
interleave("12", "abc") = {"12abc", "1a2bc", "1ab2c", "1abc2", "a12bc", "a1b2c", "a1bc2", "ab12c", "ab1c2" "abc12"}
So everytime we added a character to the first string, we formed the new result set by adding the new character to all possible positions in the old result set. Let's look at exactly how we formed the third result above from the second. How did each element in the second result turn into elements in the third result when we added the "2"?
"1abc" => "12abc", "1a2bc", "1ab2c", "1abc2"
"a1bc" => "a12bc", "a1b2c", "a1bc2"
"ab1c" => "ab12c", "ab1c2"
"abc1" => "abc12"
Now look at things this way:
"1abc" => {1 w | w = interleave("2", "abc")}
"a1bc" => {a1 w | w = interleave("2", "bc")}
"ab1c" => {ab1 w | w = interleave("2", "c")}
"abc1" => {abc1 w | w = interleave("2", "")}
Although one or two examples doesn't prove a rule in general, in this case you should be able to infer what the rule is. You will have a loop, with recursive calls inside it.
This is actually a little more fun to do with pure functional programming, but you tagged the question with Java.
Hopefully this is a start for you. If you get stuck further you can do a web search for "interleaving strings" or "interleaving lists". There are some solutions out there.
EDIT:
Okay I just wrote the silly thing! It's a lot of fun to write these things in scripting languages, so I thought it would be great to see what it looked like in Java. Not as bad as I thought it would be! Here it is, packaged as an entire Java application.
import java.util.ArrayList;
import java.util.List;
public class Interleaver {
/**
* Returns a list containing all possible interleavings of two strings.
* The order of the characters within the strings is preserved.
*/
public static List<String> interleave(String s, String t) {
List<String> result = new ArrayList<String>();
if (t.isEmpty()) {
result.add(s);
} else if (s.isEmpty()) {
result.add(t);
} else {
for (int i = 0; i <= s.length(); i++) {
char c = t.charAt(0);
String left = s.substring(0, i);
String right = s.substring(i);
for (String u : interleave(right, t.substring(1))) {
result.add(left + c + u);
}
}
}
return result;
}
/**
* Prints some example interleavings to stdout.
*/
public static void main(String[] args) {
System.out.println(interleave("", ""));
System.out.println(interleave("a", ""));
System.out.println(interleave("", "1"));
System.out.println(interleave("a", "1"));
System.out.println(interleave("ab", "1"));
System.out.println(interleave("ab", "12"));
System.out.println(interleave("abc", "12"));
System.out.println(interleave("ab", "1234"));
}
}
If I interpreted your question correctly - that you want all the permutations of all the characters in both strings, then the following code will help. You will need to write your own swap function, and somehow obtain an array of all the characters in both strings.
This algorithm will permute from the i'th element up to the n'th element in the array. It is in C++, I would include a reference to where the algorithm is from but I can't remember.
void getPermutationsR(char characters[], int n, int i)
{
if (i == n)
{
//Output the current permutation
}
else
{
for (int j=i; j<n; j++)
{
swap (characters, i, j);
getPermutationsR(characters, n, i+1);
swap (characters, i, j);
}
}
}
What you have now is a good start. The problem is that it returns just one string, instead a list of those.
Change your function to return a list of string, and then think about how you could combine several lists to produce all the output you want.
Here is a solution using recursive approach, easy to understand too
public class Interleave {
public static List<String> interleave(String first, String second){
if(first.length() == 0){
List<String> list = new ArrayList<String>();
list.add(second);
return list;
}
else if(second.length() == 0){
List<String> list = new ArrayList<String>();
list.add(first);
return list;
}
else{
char c1 = first.charAt(0);
List<String> listA = multiply(c1,interleave(first.substring(1),second));
char c2 = second.charAt(0);
List<String> listB = multiply(c2,interleave(first,second.substring(1)));
listA.addAll(listB);
return listA;
}
}
public static List<String> multiply(char c,List<String> list){
List<String> result = new ArrayList<String>();
for(String str : list){
String res = Character.toString(c) + str;
result.add(res);
}
return result;
}
public static void main(String[] args){
System.out.println(interleave("ab", "1234"));
System.out.println(interleave("a", "b"));
System.out.println(interleave("ab", "cd"));
}
}
Below is the much better and simple to understand solution for this problem:
public class Interleaver {
/**
* Returns a list containing all possible interleavings of two strings.
* The order of the characters within the strings is preserved.
*/
public static String s1 = "abc";
public static String s2 = "12";
public static void interleave(int i, int j, String s) {
if (i == s1.length() && j == s2.length()) {
System.out.println("" + s);
}
if (i != s1.length()) {
interleave(i + 1, j, s + s1.charAt(i));
}
if (j != s2.length()) {
interleave(i, j + 1, s + s2.charAt(j));
}
}//Method ends here
/**
* Prints some example interleavings to stdout.
*/
public static void main(String[] args) {
interleave(0, 0, "");
}//Method ends here
}//Class ends here
Program is using just simple recursive calls to find the solution.
Here is another recursive solution:
public class Interleaving2 {
public static void main(String[] args) {
String x = "ab";
String y = "CD";
int m = x.length();
int n = y.length();
char[] result = new char[m + n + 1];
interleave(x, y, result, m, n, 0);
}
public static void interleave(String x, String y, char[] result, int m, int n, int i) {
if (m == 0 && n == 0) {
System.out.println(String.valueOf(result));
}
if (m != 0) {
result[i] = x.charAt(0);
interleave(x.substring(1), y, result, m - 1, n, i + 1);
}
if (n != 0) {
result[i] = y.charAt(0);
interleave(x, y.substring(1), result, m, n - 1, i + 1);
}
}
}

Categories

Resources