Can't produce all permutations of a String (iteratively) - java

So I'm working on some Java exercises, and one that has caught my attention recently is trying to produce all permutations of a String using iteration. There are plenty of examples online - however, a lot of them seem very complex and I'm not able to follow.
I have tried using my own method, which when tested with a string of length 3 it works fine. The method is to (for each letter) keep moving a letter along the string, swapping it with whatever letter is in front of it. E.g.
index: 012
string: abc
(iteration 1) swap 'a' (index 0) with letter after it 'b' (index 0+1) : bac
(iteration 2) swap 'a' (index 1) with letter after it 'c' (index 1+1) : bca
(iteration 3) swap 'a' (index 2) with letter after it 'b' (index 0) : acb
current permutations: abc (original), bac, bca, acb
(iteration 3) swap 'b' (index 1) with letter after it 'c' (index 1+1) : acb
(iteration 4) swap 'b' (index 2) with letter after it 'a' (index 0) : bca
(iteration 5) swap 'b' (index 0) with letter after it 'c' (index 1) : cba
current permutations: abc (original), bac, bca, acb, acb, cba
...
This is how I implemented it in Java:
String str = "abc"; // string to permute
char[] letters = str.toCharArray(); // split string into char array
int setLength = factorial(letters.length); // amount of permutations = n!
HashSet<String> permutations = new HashSet<String>(); // store permutations in Set to avoid duplicates
permutations.add(str); // add original string to set
// algorithm as described above
for (int i = 0; i < setLength; i++) {
for (int j = 0; j < letters.length; j++) {
int k;
if (j == letters.length - 1) {
k = 0;
} else {
k = j + 1;
}
letters = swap(letters, j, k);
String perm = new String(letters);
permutations.add(perm);
}
}
The problem is if I input a string of length 4, I only end up with 12 permutations (4x3) - if I input a string of length 5, I only end up with 20 permutations (5x4).
Is there a simple modification I could make to this algorithm to get every possible permutation? Or does this particular method only work for strings of length 3?
Appreciate any feedback!

Suppose the input is "abcd". This is how your algorithm will work
bacd
bacd
bcad
bcda
If you observe carefully, "a" was getting positioned at all indexes and the following consecutive letter was getting replaced with "a". However, after your algorithm has produced "bacd" - it should be followed by "badc" also, which will be missing from your output.
For string of length 4, When you calculated the number of permutations as factorial, you understand that the first position can be occupied by 4 characters, followed by 3, 2 and 1. However, in your case when the first two positions are occupied by "ba" there are two possibilities for 3rd position, i.e. c and d. While your algorithm correctly finds "cd", it fails to find "dc" - because, the loop does not break the problem into further subproblems, i.e. "cd" has two permutations, respectively "cd" and "dc".
Thus, the difference in count of your permutations and actual answer will increase as the length of string increases.
To easily break problems into sub-problem and solve it, many algorithm uses recursion.
However, you could look into Generate list of all possible permutations of a string for good iterative answers.
Also, as the length of string grows, calculating number of permutation is not advisable.

While I do not know of a way to expand upon your current method of switching places (I've attempted this before to no luck), I do know of a fairly straightforward method of going about it
//simple method to set up permutate
private static void permutations(String s)
{
permutate(s, "");
}
//takes the string of chars to swap around (s) and the base of the string to add to
private static void permutate(String s, String base)
{
//nothing left to swap, just print out
if(s.length() <= 1)
System.out.println(base + s);
else
//loop through the string of chars to flip around
for(int i = 0; i < s.length(); i++)
//call with a smaller string of chars to flip (not including selected char), add selected char to base
permutate(s.substring(0, i) + s.substring(i + 1), base + s.charAt(i));
}
The goal with this recursion is to delegate as much processing as possible to something else, breaking the problem down bit by bit. It's easy to break down this problem by choosing a char to be first, then telling a function to figure out the rest. This can then be done for each char until they've all been chosen once

Related

Recursion: Longest Palindrome Substring

This is a very common problem in which we would have to find the longest substring which is also a palindrome substring for the given input string.
Now there are multiple possible approaches to this and I am aware about Dynamic programming solution, expand from middle etc. All these solutions should be used for any practical usecase.
I was experimenting with using recursion to solve this problem and trying to implement the simple idea.
Let us assume that s is the given input string and i and j represent any valid character indexes of input string. So if s[i] == s[j], my longest substring would be:
s.charAt(i) + longestSubstring(s, i + 1, j - 1) + s.charAt(j)
And if these two characters are not equal then:
max of longestSubstring(s, i + 1, j) or longestSubstring(s, i, j - 1)
I tried to implement this solution below:
// end is inclusive
private static String longestPalindromeHelper(String s, int start, int end) {
if (start > end) {
return "";
} else if (start == end) {
return s.substring(start, end + 1);
}
// if the character at start is equal to end
if (s.charAt(start) == s.charAt(end)) {
// I can concatenate the start and end characters to my result string
// plus I can concatenate the longest palindrome in start + 1 to end - 1
// now logically this makes sense to me, but this would fail in the case
// for ex: a a c a b d k a c a a (space added for visualization)
// when start = 3 (a character)
// end = 7 (again end character)
// it will go in recursion with start = 4 and end = 6 from now onwards
// there is no palindrome substrings apart from the single character
// substring (which are palindrome by itself) so recursion tree for
// start = 3 and end = 7 would return any single character from b d k
// let's say it returns b so result would be a a c a b a c a a
// this would be correct answer for longest palindrome subsequence but
// not substring because for sub strings I need to have consecutive
// characters
return s.charAt(start)
+ longestPalindromeHelper(s, start + 1, end - 1) + s.charAt(end);
} else {
// characters are not equal, increment start
String s1 = longestPalindromeHelper(s, start + 1, end);
String s2 = longestPalindromeHelper(s, start, end - 1);
return s1.length() > s2.length() ? s1 : s2;
}
}
public static String longestPalindrome(String s) {
return longestPalindromeHelper(s, 0, s.length() - 1);
}
public static void main(String[] args) throws Exception {
String ans = longestPalindrome("aacabdkacaa");
System.out.println("Answer => " + ans);
}
For a moment let us forgot about time complexity or runtime. I am focused towards making it work for simple case above.
As you can see in the comments I got the idea why this is failing but I tried hard to rectify the problem following the exactly same approach. I don't want to use loops here.
What could be the possible fix for this following same approach?
Note: I am interested in the actual string as answer and not the length. FYI I had a look at all the other questions and it seems no one is following this approach for correctness so I am trying.
Once you have a call wherein s[i] == s[j], you could flip a boolean flag or switch to a modified method that communicates to child calls that they can no longer use the "don't match, try i + 1 and j - 1" branch (else condition). This ensures you're looking at substrings, not subsequences, for the remainder of the recursion.
Secondly, for the substring variant, even if s[i] == s[j], you should also try i + 1 and j - 1 as if these characters didn't match, because one or both of these characters might not be part of the final best substring between i and j. In the subsequence version, there's never any reason not to add any matching characters to the current palindromic subsequence for the range i to j, but that's not always the case with substrings.
For example, given input "aabcbda" and we're at a call frame where i = 1 and j = length - 1, we need to maximize over three possibilities:
The best substring includes both 'a' characters. Call the subroutine with the flag that says we have to consume from both ends on down and can no longer try skipping characters.
The best substring might still include s[i] but not s[j], try j - 1.
The best substring might still include s[j] but not s[i], try i + 1.
Another observation: it might make more sense to pass best indices up the helper call chain, then grab the longest palindromic substring based on these indices at the very end in the wrapper function.
On a similar note, if you're struggling, you might simplify the problem and return the longest palindromic substring length using your recursive method, then switch to getting the actual substring itself. This makes it easier to focus on the subsequence logic without the return value complicating things as much.
It is much easier to use loops here, rather than recursion, something like this:
public static void main(String[] args) {
System.out.println(longestPalindrome("abbqa")); // bb
System.out.println(longestPalindrome("aacabdkacaa")); // aca
System.out.println(longestPalindrome("aacabdkaccaa")); // acca
}
public static String longestPalindrome(String str) {
String palindrome = "";
for (int i = 0; i < str.length(); i++) {
for (int j = i; j < str.length(); j++) {
String substring = str.substring(i, j);
if (isPalindrome(substring)
&& substring.length() > palindrome.length()) {
palindrome = substring;
}
}
}
return palindrome;
}
public static boolean isPalindrome(String str) {
for (int i = 0; i < str.length() / 2; i++) {
if (str.charAt(i) != str.charAt(str.length() - i - 1)) {
return false;
}
}
return true;
}

Need Faster Word Builder Algorithm

I have an app that assists studying for Scrabble. Most searches are much faster than by desktop version in C#, except the Word Builder. This search shows all the words that can be formed from a given set of letters A-Z, or blanks.
What can I do to get it to run faster?
I've considered using a Trie, but haven't found a way to support the use of blanks.
I am using a SimpleCursorAdapter to populate the ListView, which is why I am returning a cursor.
public Cursor getCursor_subanagrams(String term, String filters, String ordering) {
if (term.trim() == "")
return null;
// only difference between this and anagram is changing the length filter
char[] a = term.toCharArray(); // anagram
int[] first = new int[26]; // letter count of anagram
int c; // array position
int blankcount = 0;
// initialize word to anagram
for (c = 0; c < a.length; c++) {
if (a[c] == '?') {
blankcount++;
continue;
}
first[a[c] - 'A']++;
}
// gets pool of words to search through
String lenFilter = String.format("Length(Word) <= %1$s AND Length(Word) <= %2$s", LexData.getMaxLength(), term.length());
Cursor cursor = database.rawQuery("SELECT WordID as _id, Word, WordID, FrontHooks, BackHooks, " +
"InnerFront, InnerBack, Anagrams, ProbFactor, OPlayFactor, Score \n" +
"FROM `" + LexData.getLexName() + "` \n" +
"WHERE (" + lenFilter +
filters +
" ) " + ordering, null);
// creates new cursor to add valid words to
MatrixCursor matrixCursor = new MatrixCursor(new String[]{"_id", "Word", "WordID", "FrontHooks", "BackHooks", "InnerFront", "InnerBack",
"Anagrams", "ProbFactor", "OPlayFactor", "Score"});
// THIS NEEDS TO BE FASTER
while (cursor.moveToNext()) {
String word = cursor.getString(1);
char[] b = word.toCharArray();
if (isAnagram(first, b, blankcount)) {
matrixCursor.addRow(get_CursorRow(cursor));
}
}
cursor.close();
return matrixCursor;
}
private boolean isAnagram(int[] anagram, char[] word, int blankcount) {
int matchcount = blankcount;
int c; // each letter
int[] second = {0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0};
for (c = 0; c < word.length; c++)
second[word[c] - 'A']++;
for (c = 0; c < 26; c++)
{
matchcount += (anagram[c]<second[c]) ? anagram[c]:second[c];
}
if (matchcount == word.length)
return true;
return false;
}
Focus on speeding up the most typical case, which is where the word is not a (sub)anagram, and you return false. If you can identify as quickly as possible when it is not possible to make word out of anagram then you can avoid the expensive test.
One way to do this is using a bitmask of the letters in the words. You don't need to store letter counts, because if the number of unique letters in word that are not in anagram is greater than the number of blanks, then there is no way you can make it and you can quickly return false. If not then you can proceed to the more expensive test taking letter counts into account.
You can precompute the bitmasks like this:
private int letterMask(char[] word)
{
int c, mask = 0;
for (c = 0; c < word.length; c++)
mask |= (1 << (word[c] - 'A'));
return mask;
}
Add an extra column to your database to store the letter bitmask for each word, add it to your cursor, and compute the letter bitmask for the letters in term and store in termMask. Then inside your cursor loop you can do a test like this:
// compute mask of bits in mask that are not in term:
int missingLettersMask = cursor.getInt(8) & ~termMask;
if(missingLettersMask != 0)
{
// check if we could possibly make up for these letters using blanks:
int remainingBlanks = blankcount;
while((remainingBlanks-- > 0) && (missingLettersMask != 0))
missingLettersMask &= (missingLettersMask - 1); // remove one bit
if(missingLettersMask != 0)
continue; // move onto the next word
}
// word can potentially be made from anagram, call isAnagram:
There are ways to speed up your anagram checking function. Samgak has pointed out one. Another obvious optimisation is to return false if the word is longer than the number of available letters plus blanks. In the end, these are all micro-optimisations and you will end up checking your whole dictionary.
You said you have considered using a trie. That's a good solution, in my opinion, because the structure of the trie will only make you check relevant words. Build it like this:
Sort the letters of each word, so that "triangle" and "integral" will both become "aegilnrt".
Insert the sorted word into the trie.
Where you would place the end-marker in a normal trie, you place a list of possible words.
If you were looking for exact anagrams, you would sort the word to check, traverse the trie and print out the list of possible anagrams at the end. But here, you have to deal with partial anagrams and with blanks:
Regular traversal means you take the next letter of the word and then descent the corresponding link in the tree,if it exists.
Partial anagrams can be found by ignoring the next letter without descending in the trie.
Blanks can be dealt with by descending all possible branches of a trie and decreasing the number of blanks.
When you have blanks, you will end up with duplicates. For example, if you have the letters A, B and C and a blank tile, you can make the word CAB, but you can get there on four different ways: CAB, _AB, C_B, CA_.
You could get around this by storing the result list in a data structure that eliminates duplicates, such as a set or ordered set, but you would still go down the same paths several times in order to create the duplicates.
A better solution is to keep track of which trie nodes you have visited with which parameters, i.e. with with remaining unused letters and blanks. You can then cut such paths short. Here's an implementation in pseudocode:
function find_r(t, str, blanks, visited)
{
// don't revisit explored paths
key = make_key(t, str, blanks);
if (key in visited) return [];
visited ~= key;
if (str.length == 0 and blanks == 0) {
// all resources have been used: return list of anagrams
return t.word;
} else {
res = [];
c = 0;
if (str.length > 0) {
c = str[0];
// regular traversal: use current letter and descend
if (c in t.next) {
res ~= find_r(t.next[c], str[1:], blanks, visited);
}
# partial anagrams: skip current letter and don't descend
l = 1
while (l < str.length and str[l] == c) l++;
res ~= find_r(t, str[l:], blanks, visited);
}
if (blanks > 0) {
// blanks: decrease blanks and descend
for (i in t.next) {
if (i < c) {
res ~= find_r(t.next[i], str, blanks - 1, visited);
}
}
}
return res;
}
}
(Here, ~ denotes list concatenation or set insertion; [beg=0:end=length] denotes string slices; in tests whether a dictionary or set contains a key.)
Once you've built the tree, this solution is fast when there are no blanks, but it gets exponentially worse with each blank and with larger letter pools. Testing with one blank is still reasonably fast, but with two blanks, it is on par with your existing solution.
Now there are at most two blanks in a Scrabble game and the rack can hold only up to seven tiles, so it may not be as bad in practice. Another question is whether the search should consider words obtained with two blanks. The results list will be very long and it will contain all two-letter words. The player may be more interested in high-scoring words that can be played with a single blank.

Running time of permutation function

My book provides the following code for a function that computes all the permutations of a string of unique characters (see code below), and says that the running time is O(n!), "since there are n! permutations."
I don't understand how they've computed the running time as O(n!). I assume they mean "n" is the length of the original string. I think that the running time should be something like O((n + 1)XY), since the getPerms function will be called (n + 1) times, and X and Y can represent the running times of the outer and inner for loops respectively. Can someone explain to me why this is wrong / the book's answer is right?
Thanks.
public static ArrayList<String> getPerms(String str)
{
if (str == null)
return null;
ArrayList<String> permutations = new ArrayList<String>();
if (str.length() == 0)
permutations.add("");
return permutations;
char first = str.charAt(0); //first character of string
String remainder = str.substring(1); //remove first character
ArrayList<String> words = getPerms(remainder);
for (String word: words)
{
for (i = 0; i <= word.length(); i++)
{
String s = insertCharAt(word, first, i);
permutations.add(s)
}
}
return permutations;
}
public static String insertCharAt(String word, char c, int j)
{
String start = word.substring(0, i);
String end = word.substring(i);
return start + c + end;
}
Source: Cracking the Coding Interview
From our intuition, it is clear that there is no existing algorithm that generate permutation of N items that perform better than O(n!) because there are n! possibility.
You can reduce the recursive code into recurrence equation because gePerm(n) where n is a string with n length will call getPerm(n-1). Then, we use all the value returns by it and put a inner loop that loop N times. So we have
Pn = nPn-1
P1 = 1
It is easy to see that Pn = n! by telescoping the equation.
If you have hard times visualize how we come up with this equation, you can also think of this way
ArrayList<String> words = getPerms(remainder);
for (String word: words) // P(n-1)
{
for (i = 0; i <= word.length(); i++) // nP(n-1)
{
String s = insertCharAt(word, first, i);
permutations.add(s)
}
}
The count of permutations of N elements is N * (N - 1) * (N - 2) * ... * 2 * 1, i.e. N!.
First character can be any one of N characters. Next character can be one of remained N - 1 characters. Now we have N * (N - 1) possible cases already.
So, continuing we'll have N * (N - 1) * (N - 2) * ... cases at each step.
Cause the count of permutations of N elements is N!, then there isn't an implementation that can permutate an array of length N faster than N!.

Why no out of bounds exception?

I am looking at a problem which asks to return the number of the positions where two given strings contain the same length 2 substring. So "xxcaazz" and "xxbaaz" yields 3, since the "xx", "aa", and "az" substrings appear in the same place in both strings. The solution is said to be the following:
public int stringMatch(String a, String b) {
// Figure which string is shorter.
int len = Math.min(a.length(), b.length());
int count = 0;
// Look at both substrings starting at i
for (int i=0; i<len-1; i++) {
String aSub = a.substring(i, i+2);
String bSub = b.substring(i, i+2);
if (aSub.equals(bSub)) { // Use .equals() with strings
count++;
}
}
return count;
}
I don't understand why there is no out of bounds exception for this solution. If for example, there are two string inputted with length 6 and 7 respectively, in the final iteration of the for loop, i=5. But then for the substring of the smaller string, the parameters given would be (5,7) even though the final index of the string is 5. In previous problems I seem to have produced an out of bounds exception in a similar circumstance. Why not here? All help greatly appreciated.
If we suppose that you're coding in Java, in the method substring(int beginIndex, int endIndex), endIndex is exclusive.
From http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#substring-int-int- :
Parameters:
beginIndex - the beginning index, inclusive.
endIndex - the ending index, exclusive.
So when you're calling your last indent, i is equals to 4 because i<len-1 in the for condition; so:
String bSub = b.substring(i, i+2);
=> b.substring(4, 6) => xxba az
If you want an StringIndexOutOfBoundsException, remove -1 in your for condition.
As you were mentioning "In the final iteration of the for loop, i=5". In the 5th iteration the i = 4 you are starting from 0th index. So the output is
0> xx == xx
1> xc == xb
2> ca == ba
3> aa == aa
4> az == az
For the substring function the second parameter in exclusive so substring(4,6) never tries to read the index 6. Thus, the program does not result IndexOutOfBoundsException.

Why am I getting java.lang.StringIndexOutOfBoundsException?

I want to write a program that prints words incrementally until a complete sentence appears. For example : I need to write (input), and output:
I
I need
I need to
I need to write.
Here is my code:
public static void main(String[] args) {
String sentence = "I need to write.";
int len = sentence.length();
int numSpace=0;
System.out.println(sentence);
System.out.println(len);
for(int k=0; k<len; k++){
if(sentence.charAt(k)!='\t')
continue;
numSpace++;
}
System.out.println("Found "+numSpace +"\t in the string.");
int n=1;
for (int m = 1; m <=3; m++) {
n=sentence.indexOf('\t',n-1);
System.out.println("ligne"+m+sentence.substring(0, n));
}
}
and this is what I get:
I need to write.
16
Found 0 in the string.
Exception in thread "main" java.lang.StringIndexOutOfBoundsException:
String index out of range: -1 at
java.lang.String.substring(String.java:1937) at
split1.Split1.main(Split1.java:36) Java Result: 1 BUILD SUCCESSFUL
(total time: 0 seconds)
I don't understand why numSpace doesn't count the occurrences of spaces, nor why I don't get the correct output (even if I replace numSpace by 3 for example).
You don't have a \t character, so indexOf(..) returns -1
You try a substring from 0 to -1 - fails
The solution is to check:
if (n > -1) {
System.out.prinltn(...);
}
Your loop looking for numSpace is incorrect. You are looking for a \t which is a tab character, of which there are none in the string.
Further, when you loop in the bottom, you get an exception because you are trying to parse by that same\t, which will again return no results. The value of n in n=sentence.indexOf('\t',n-1); is going to return -1 which means "there is not last index of what you are looking for". Then you try to get an actual substring with the value of -1 which is an invalid substring, so you get an exception.
You are mistaken by the concept of \t which is an escape sequence for a horizontal tab and not for a whitespace character (space). Searching for ' ' would do the trick and find the whitespaces in your sentence.
This looks like homework, so my answer is a hint.
Hint: read the javadoc for String.indexOf paying attention to what it says about the value returned when the string / character is not found.
(In fact - even if this is not formal homework, you are clearly a Java beginner. And beginners need to learn that the javadocs are the first place to look when using an unfamiliar method.)
The easiest way to solve this I guess would be to split the String first by using the function String.split. Something like this:
static void sentence(String snt) {
String[] split = snt.split(" ");
for (int i = 0; i < split.length; i++) {
for (int j = 0; j <= i; j++) {
if (i == 1 && j == 0) System.out.print(split[j]);
else System.out.printf(" %s", split[j]);
}
}
}
As other people pointed out. You are counting every characters except tabs(\t) as a space. You need to check for spaces by
if (sentence.charAt(k) == ' ')
\t represents a tab. To look for a space, just use ' '.
.indexOf() returns -1 if it can't find a character in the string. So we keep looping until .indexOf() returns -1.
Use of continue wasn't really needed here. We increment numSpaces when we encounter a space.
System.out.format is useful when we want to mix literal strings and variables. No ugly +s needed.
String sentence = "I need to write.";
int len = sentence.length();
int numSpace = 0;
for (int k = 0; k < len; k++) {
if (sentence.charAt(k) == ' ') {
numSpace++;
}
}
System.out.format("Found %s in the string.\n", numSpace);
int index = sentence.indexOf(' ');
while(index > -1) {
System.out.println(sentence.substring(0, index));
index = sentence.indexOf(' ', index + 1);
}
System.out.println(sentence);
}
Try this, it should pretty much do what you want. I figure you have already finished this so I just made the code real fast. Read the comments for the reasons behind the code.
public static void main(String[] args) {
String sentence = "I need to write.";
int len = sentence.length();
String[] broken = sentence.split(" "); //Doing this instead of the counting of characters is just easier...
/*
* The split method makes it where it populates the array based on either side of a " "
* (blank space) so at the array index of 0 would be 'I' at 1 would be "need", etc.
*/
boolean done = false;
int n = 0;
while (!done) { // While done is false do the below
for (int i = 0; i <= n; i++) { //This prints out the below however many times the count of 'n' is.
/*
* The reason behind this is so that it will print just 'I' the first time when
* 'n' is 0 (because it only prints once starting at 0, which is 'I') but when 'n' is
* 1 it goes through twice making it print 2 times ('I' then 'need") and so on and so
* forth.
*/
System.out.print(broken[i] + " ");
}
System.out.println(); // Since the above method is a print this puts an '\n' (enter) moving the next prints on the next line
n++; //Makes 'n' go up so that it is larger for the next go around
if (n == broken.length) { //the '.length' portion says how many indexes there are in the array broken
/* If you don't have this then the 'while' will go on forever. basically when 'n' hits
* the same number as the amount of words in the array it stops printing.
*/
done = true;
}
}
}

Categories

Resources