Understanding this algorithm to find permutations of a string (recursion) - java

I was going through my textbook, and I couldn't really wrap my head around how this generates the permutations of a string recursively
class PermutationIterator
{
private String wrd;
private int current;
private PermutationIterator Iter;
private String tl;
// Constructor
public PermutationIterator(String s)
{
wrd = s;
current = 0;
if (wrd.length() > 0)
Iter = new PermutationIterator(wrd.substring(1));
}
public String nextPermutation()
{
if(wrd.length() == 0)
{
current++;
return "";
}
char c = wrd.charAt(current);
String nextPermut = Iter.nextPermutation();
if(!Iter.hasMorePermutations())
{
System.out.println("Current value is " + current + " word length is " + wrd.length());
current++;
if (current >= wrd.length()) {
Iter = null;
}
else
{
if (current + 1 >= wrd.length())
tl = wrd.substring(0,current);
else
//System.out.println("Reached");
tl = wrd.substring(0,current) + wrd.substring(current + 1, wrd.length());
Iter = new PermutationIterator(tl);
}
}
return c + nextPermut;
}
public boolean hasMorePermutations()
{
System.out.println("Inside this method we have current= " + current + " with wrdlength " + wrd.length() +"with the word " + wrd);
return current < wrd.length();
}
}
This gets called by
public static void main(String [] args)
{
PermutationIterator iter = new PermutationIterator("eat");
while(iter.hasMorePermutations())
{
System.out.println(iter.nextPermutation());
}
}
For eat this will output
eat
eta
aet
ate
tea
tae
My attempt
Before even attempting to understand everything, for the past three days I have been really struggling to figure out how exactly !Iter.hasMorePermutations() is reached. The only way this can be false is if return current < wrd.length(); is not true. i.e wrd.length() <= current.
Now here is where it really starts to lose me. I tried printing out the values of word.length and current inside the !Iter.hasMorePermutations() branch just to see what was going on.
Current value is 0 word length is 1
Current value is 0 word length is 2
eat
Wait.. How is this possible? Isn't our condition for reaching this branch, to have current value bigger than our word length? How did we ever reach this branch?
I have also attached a picture of my trying to trace the program,
Thanks for reading this!

There are four iterators active at a time, for each of the word lengths. They all have their own values of current, and the call to hasMorePermutations is checking the values of current and length on the next iterator, not itself. So you may want to instead output:
System.out.println("Current value is " + Iter.current + " word length is " + Iter.wrd.length());
To start with, all their current values are 0, so we have for 'eat':
(word = 'eat', current = 0, length = 3)
(word = 'at', current = 0, length = 2)
(word = 't', current = 0, length = 1)
(word = '', current = 0, length = 0)
Each iterator calls nextPermutation on the next, until we get to the last iterator which has its current value incremented because wrd.length() == 0. So we get:
(word = 'eat', current = 0, length = 3)
(word = 'at', current = 0, length = 2)
(word = 't', current = 0, length = 1)
(word = '', current = 1, length = 0)
This is detected in the third iterator's Iter.hasMorePermutations(), which will then increment its own current value and reset the last iterator:
(word = 'eat', current = 0, length = 3)
(word = 'at', current = 0, length = 2)
(word = 't', current = 1, length = 1)
(word = '', current = 0, length = 0)
This is similarly detected by the second iterator, which resets the last two iterators:
(word = 'eat', current = 0, length = 3)
(word = 'at', current = 1, length = 2)
(word = 'a', current = 0, length = 1)
(word = '', current = 0, length = 0)
The first iterator's call to Iter.hasMorePermutations() will then return false, so it doesn't increment its current value, giving the next String 'eta'.

I guess that what you are missing is that the test isn't this.hasMorePermutations(), but rather Iter.hasMorePermutations(), possibly confused by the fact that Iter looks like a class name rather than a field name...
So, just before your point 3 returns, that has word length 0 and current 1, that is, no more iterations, and when it returns to the parent it is the object being tested, so you enter the if-statement.

What you are printing are the values of wrd and current of this object, not of the object Iter on which !Iter.hasMorePermutations() is evaluated.
To understand what is going on you should be printing Iter.wrd.length() and Iter.current.

in your diagram at number 3 ,
it falls into this if branch
if(wrd.length() == 0)
{
current++;
return "";
}
once this branch is executed , now wrd.length() is 0 and current is 1,
so after nextPermutation hits this if branch, next immediate call to hasMorePermutations will return false

What's so complicated? The way I read the code and translate into English, the procedure is:
for(current=0 to string_len)
pick the letter at the position of current
extract it out from the string (make a string with that letter missing). (this will have a length reduced by one; am I stating the obvious here?)
recursively generate the permutations of the string so constructed at the prev step (call it tl), taking care to add the extracted letter as the first one for each of the permutation generated on the shortened tl string
Since when current reaches string_len there's no more letters to extract, it means you are done.
It's a case of "turtles all the way down" - a 3-letter iterator will use a 2-letter iterator, which will use a 1-letter iterator which will use an empty-string iterator.
At each level, the iterator will extract the current letter, create a level-1 iterator,'squeeze it dry' and discard it when it doesn't have anything to offer. Once current is at max, this iterator will report to the level+1 one that "i'm dry" and get sacked.
--
"Rubber duck debugging" to understand how !Iter.hasMorePermutations() is reached:
Empty string
set current = 0
extract first letter - I cannot current == len, thus:
return empty string first time we are called (and increment current)
when called, tell the caller 'false==hasMorePermutation()'
See
if(wrd.length() == 0)
{
current++;
return "";
}
Word with 1 letter - make it "t"
set current=0
extract the letter at current - first_letter='t'; tl=""
generate the first permutation of tl (empty string) using an empty string permutation iterator (see above)
String nextPermut = Iter.nextPermutation();
only one possible, as it is an empty string. When I, the one-letter-iterator, gets to ask my sub-iterator (the empty string one) "do you have more permutations" the answer will come in the negative
if(!Iter.hasMorePermutations())
// this is true immediately for my empty-str subiterator
So, what me, the one-letter-iterator will do?
Will increment my current then...
because next time I'll be asked, I won't be able to generate one more, I'll sack my (zero-length) iterator
if (current >= wrd.length()) {
Iter = null;
}
and then return the extracted letter (my only one) prepended to the permutation generated by my (already sacked) iterator : thus returning a "t" prepended to an empty string.
What I, the one-letter iterator will answer next time I'm asked hasMorePermutations? Well, false, because I incremented my current to the length of my string (1)
Do you really want me to continue, or are you already seeing the 'iterators using iterators for one-less-letter words, each one of those iterators with its own current and word'?

Related

Simple Java question using while loop, substring, and indexOf

I'm working on an exercise for learning Java where I am supposed to write a method to print to the screen all items that come after the word "category:". This is my attempt at it:
public static void main(String[] args) {
String str = "We have a large inventory of things in our warehouse falling in "
+ "the category:apperal and the slightly "
+ "more in demand category:makeup along with the category:furniture and _.";
printCategories(str);
}
public static void printCategories(String passedString) {
int startOfSubstring = passedString.indexOf(":") + 1;
int endOfSubstring = passedString.indexOf(" ", startOfSubstring);
String categories = passedString.substring(startOfSubstring,endOfSubstring);
while(startOfSubstring > 0) {
System.out.println(categories);
startOfSubstring = passedString.indexOf((":") + 1, passedString.indexOf(categories));
System.out.println(startOfSubstring);
System.out.println(categories);
}
}
So the program should print:
apperal
makeup
furniture
My attempt is that the program should print the substring where it finds the starting index as ":" and the ending index as " ". Then it does the same thing again, only except from starting the very beginning of the variable str, this time it starts from the beginning of the last category found.
Once there are no more ":" to be found, the indexOf (part of startOfSubstring) will return -1 and the loop will terminate. However, after printing the first category it keeps returning -1 and terminating before finding the next category.
The two lines:
System.out.println(startOfSubstring);
System.out.println(categories);
Confirm that it is returning -1 after printing the first category, and the last line confirms that the categories variable is still defined as "apperal". If I comment out the line:
startOfSubstring = passedString.indexOf((":") + 1, passedString.indexOf(categories));
It returns the startOfSubstring as 77. So it is something to do with that line and attempting to change the start of search position in the indexOf method that is causing it to return -1 prematurely, but I cannot figure out why this is happening. I've spent the last few hours trying to figure it out...
Please help :(
There are a couple of issues with the program:
You're searching passedString for (":") + 1 which is the string ":1", probably not what you want.
You should evaluate endOfSubstring and categories inside the loop.
This is probably close to what you want:
public static void printCategories(String passedString) {
int startOfSubstring = passedString.indexOf(":") + 1;
while(startOfSubstring > 0) {
int endOfSubstring = passedString.indexOf(" ", startOfSubstring);
// If "category:whatever" can appear at the end of the string
// without a space, adjust endOfSubstring here.
String categories = passedString.substring(startOfSubstring, endOfSubstring);
// Do something with categories here, maybe print it?
// Find next ":" starting with end of category string.
startOfSubstring = passedString.indexOf(":", endOfSubstring) + 1;
}
}
I have corrected (in a comment) where you set the new value of startOfSubstring
while(startOfSubstring > 0) { // better if you do startOfSubstring != -1 IMO
System.out.println(categories);
// this should be startOfSubstring = passedString.indexOf(":", startOfSubstring +1);
startOfSubstring = passedString.indexOf((":") + 1, passedString.indexOf(categories));
System.out.println(startOfSubstring);
System.out.println(categories);
}

Length of the Longest Common Substring without repeating characters

Given "abcabcbb", the answer is "abc", which the length is 3.
Given "bbbbb", the answer is "b", with the length of 1.
Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
I have came up with a solution that worked, but failed for several test cases. I then found a better solution and I rewrote it to try and understand it. The solution below works flawlessly, but after about 2 hours of battling with this thing, I still can not understand why this particular line of code works.
import java.util.*;
import java.math.*;
public class Solution {
public int lengthOfLongestSubstring(String str) {
if(str.length() == 0)
return 0;
HashMap<Character,Integer> map = new HashMap<>();
int startingIndexOfLongestSubstring = 0;
int max = 0;
for(int i = 0; i < str.length(); i++){
char currentChar = str.charAt(i);
if(map.containsKey(currentChar))
startingIndexOfLongestSubstring = Math.max(startingIndexOfLongestSubstring, map.get(currentChar) + 1);
map.put(currentChar, i);
max = Math.max(max, i - startingIndexOfLongestSubstring + 1);
}//End of loop
return max;
}
}
The line in question is max = Math.max(max, i - startingIndexOfLongestSubstring + 1);
I don't understand why this works. We're taking the max between our previous max, and the difference between our current index and the starting index of what is currently the longest substring and then adding 1. I know that the code is getting the difference between our current index, and the startingIndexOfSubstring, but I can't conceptualize WHY it works to give us the intended result; Can someone please explain this step to me, particularly WHY it works?
I'm usually bad at explaining, let me give it a shot by considering an example.
String is "wcabcdeghi".
Forget the code for a minute and assume we're trying to come up with a logic.
We start from w and keep going until we reach c -> a -> b -> c.
We need to stop at this point because "c" is repeating. So we need a map to store if a character is repeated. (In code : map.put(currentChar, i); )
Now that we know if a character is repeated, We need to know what is the max. length so far. (In code -) max
Now we know there is no point in keeping track of count of first 2 variables w->c. This is because including this, we already got the Max. value. So from next iteration onwards we need to check length only from a -> b -> soon.
Lets have a variable (In code -)startingIndexOfLongestSubstring to keep track of this. (This should've been named startingIndexOfNonRepetativeCharacter, then again I'm bad with naming as well).
Now we again keep continuing, but wait we still haven't finalized on how to keep track of sub-string that we're currently parsing. (i.e., from abcd...)
Coming to think of it, all I need is the position of where "a" was present (which is startingIndexOfNonRepetativeCharacter) so to know the length of current sub-string all I need to do is (In code -)i - startingIndexOfLongestSubstring + 1 (current character position - The non-repetative character length + (subtraction doesn't do inclusive of both sides so adding 1). Lets call this currentLength
But wait, what are we going to do with this count. Every time we find a new variable we need to check if this currentLength can break our max.
So (In code -) max = Math.max(max, i - startingIndexOfLongestSubstring + 1);
Now we've covered most of the statements that we need and according to our logic everytime we encounter a variable which was already present all we need is startingIndexOfLongestSubstring = map.get(currentChar). So why are we doing a Max?
Consider a scenario where String is "wcabcdewghi". when we start processing our new counter as a -> b -> c -> d -> e -> w At this point our logic checks if this character was present previously or not. Since its present, it starts the count from index "1". Which totally messes up the whole count. So We need to make sure, the next index we take from map is always greater than the starting point of our count(i.e., select a character from the map only if the character occurs before startingIndexOfLongestSubstring).
Hope I've answered all lines in the code and mainly If the explanation was understandable.
Because
i - startingIndexOfLongestSubstring + 1
is amount of characters between i and startingIndexOfLongestSubstring indexes. For example how many characters between position 2 and 3? 3-2=1 but we have 2 characters: on position 2 and position 3.
I've described every action in the code:
public class Solution {
public int lengthOfLongestSubstring(String str) {
if(str.length() == 0)
return 0;
HashMap<Character,Integer> map = new HashMap<>();
int startingIndexOfLongestSubstring = 0;
int max = 0;
// loop over all characters in the string
for(int i = 0; i < str.length(); i++){
// get character at position i
char currentChar = str.charAt(i);
// if we already met this character
if(map.containsKey(currentChar))
// then get maximum of previous 'startingIndexOfLongestSubstring' and
// map.get(currentChar) + 1 (it is last occurrence of the current character in our word before plus 1)
// "plus 1" - it is because we should start count from the next character because our current character
// is the same
startingIndexOfLongestSubstring = Math.max(startingIndexOfLongestSubstring, map.get(currentChar) + 1);
// save position of the current character in the map. If map already has some value for current character
// then it will override (we don't want to know previous positions of the character)
map.put(currentChar, i);
// get maximum between 'max' (candidate for return value) and such value for current character
max = Math.max(max, i - startingIndexOfLongestSubstring + 1);
}//End of loop
return max;
}
}

Find every possible subset given a string [duplicate]

This question already has answers here:
Memory efficient power set algorithm
(5 answers)
Closed 8 years ago.
I'm trying to find every possible anagram of a string in Java - By this I mean that if I have a 4 character long word I want all the possible 3 character long words derived from it, all the 2 character long and all the 1 character long. The most straightforward way I tought of is to use two nested for loops and iterare over the string. This is my code as of now:
private ArrayList<String> subsets(String word){
ArrayList<String> s = new ArrayList<String>();
int length = word.length();
for (int c=0; c<length; c++){
for (int i=0; i<length-c; i++){
String sub = word.substring(c, c+i+1);
System.out.println(sub);
//if (!s.contains(sub) && sub!=null)
s.add(sub);
}
}
//java.util.Collections.sort(s, new MyComparator());
//System.out.println(s.toString());
return s;
}
My problem is that it works for 3 letter words, fun yelds this result (Don't mind the ordering, the word is processed so that I have a string with the letters in alphabetical order):
f
fn
fnu
n
nu
u
But when I try 4 letter words, it leaves something out, as in catq gives me:
a
ac
acq
acqt
c
cq
cqt
q
qt
t
i.e., I don't see the 3 character long word act - which is the one I'm looking for when testing this method. I can't understand what the problem is, and it's most likely a logical error I'm making when creating the substrings. If anyone can help me out, please don't give me the code for it but rather the reasoning behind your solution. This is a piece of coursework and I need to come up with the code on my own.
EDIT: to clear something out, for me acq, qca, caq, aqc, cqa, qac, etc. are the same thing - To make it even clearer, what happens is that the string gets sorted in alphabetical order, so all those permutations should come up as one unique result, acq. So, I don't need all the permutations of a string, but rather, given a 4 character long string, all the 3 character long ones that I can derive from it - that means taking out one character at a time and returning that string as a result, doing that for every character in the original string.
I hope I have made my problem a bit clearer
It's working fine, you just misspelled "caqt" as "acqt" in your tests/input.
(The issue is probably that you're sorting your input. If you want substrings, you have to leave the input unsorted.)
After your edits: see Generating all permutations of a given string Then just sort the individual letters, and put them in a set.
Ok, as you've already devised your own solution, I'll give you my take on it. Firstly, consider how big your result list is going to be. You're essentially taking each letter in turn, and either including it or not. 2 possibilities for each letter, gives you 2^n total results, where n is the number of letters. This of course includes the case where you don't use any letter, and end up with an empty string.
Next, if you enumerate every possibility with a 0 for 'include this letter' and a 1 for don't include it, taking your 'fnu' example you end up with:
000 - ''
001 - 'u'
010 - 'n'
011 - 'nu'
100 - 'f'
101 - 'fu' (no offense intended)
110 - 'fn'
111 - 'fnu'.
Clearly, these are just binary numbers, and you can derive a function that given any number from 0-7 and the three letter input, will calculate the corresponding subset.
It's fairly easy to do in java.. don't have a java compiler to hand, but this should be approximately correct:
public string getSubSet(string input, int index) {
// Should check that index >=0 and < 2^input.length here.
// Should also check that input.length <= 31.
string returnValue = "";
for (int i = 0; i < input.length; i++) {
if (i & (1 << i) != 0) // 1 << i is the equivalent of 2^i
returnValue += input[i];
}
return returnValue;
}
Then, if you need to you can just do a loop that calls this function, like this:
for (i = 1; i < (1 << input.length); i++)
getSubSet(input, i); // this doesn't do anything, but you can add it to a list, or output it as desired.
Note I started from 1 instead of 0- this is because the result at index 0 will be the empty string. Incidentally, this actually does the least significant bit first, so your output list would be 'f', 'n', 'fn', 'u', 'fu', 'nu', 'fnu', but the order didn't seem important.
This is the method I came up with, seems like it's working
private void subsets(String word, ArrayList<String> subset){
if(word.length() == 1){
subset.add(word);
return;
}
else {
String firstChar = word.substring(0,1);
word = word.substring(1);
subsets(word, subset);
int size = subset.size();
for (int i = 0; i < size; i++){
String temp = firstChar + subset.get(i);
subset.add(temp);
}
subset.add(firstChar);
return;
}
}
What I do is check if the word is bigger than one character, otherwise I'll add the character alone to the ArrayList and start the recursive process. If it is bigger, I save the first character and make a recursive call with the rest of the String. What happens is that the whole string gets sliced in characters saved in the recursive stack, until I hit the point where my word has become of length 1, only one character remaining.
When that happens, as I said at the start, the character gets added to the List, now the recursion starts and it looks at the size of the array, in the first iteration is 1, and then with a for loop adds the character saved in the stack for the previous call concatenated with every element in the ArrayList. Then it adds the character on its own and unwinds the recursion again.
I.E., with the word funthis happens:
f saved
List empty
recursive call(un)
-
u saved
List empty
recursive call(n)
-
n.length == 1
List = [n]
return
-
list.size=1
temp = u + list[0]
List = [n, un]
add the character saved in the stack on its own
List = [n, un, u]
return
-
list.size=3
temp = f + list[0]
List = [n, un, u, fn]
temp = f + list[1]
List = [n, un, u, fn, fun]
temp = f + list[2]
List = [n, un, u, fn, fun, fu]
add the character saved in the stack on its own
List = [n, un, u, fn, fun, fu, f]
return
I have been as clear as possible, I hope this clarifies what was my initial problem and how to solve it.
This is working code:
public static void main(String[] args) {
String input = "abcde";
Set<String> returnList = permutations(input);
System.out.println(returnList);
}
private static Set<String> permutations(String input) {
if (input.length() == 1) {
Set<String> a = new TreeSet<>();
a.add(input);
return a;
}
Set<String> returnSet = new TreeSet<>();
for (int i = 0; i < input.length(); i++) {
String prefix = input.substring(i, i + 1);
Set<String> permutations = permutations(input.substring(i + 1));
returnSet.add(prefix);
returnSet.addAll(permutations);
Iterator<String> it = permutations.iterator();
while (it.hasNext()) {
returnSet.add(prefix + it.next());
}
}
return returnSet;
}

Need to store every other character of a string, into another

public class newString {
public static void main (String args[]){
String title = "Book";
String title1;
title1 = title;
for(int i = 0; i < title.length(); i++){
for (int x = 0; x<title1.length(); x++){
if (title.charAt(i+x) == title1.charAt(x)){
System.out.print(title.charAt(0,1));
}
}
}
}
}
I really don't understand what I'm doing wrong here. What I need to do is define a string called "title", with "Book" in it, which I did, and create a second string called "title1". I need to create code to store the contents of title, into title1, but only every other character. For example: title1 should have "Bo" in it. What am I doing wrong?
Here's the looping solution with fewer operations. Instead of checking if i is even, just increment by 2.
String title1 = "Some title";
String title2 = "";
for (int i = 0; i < title1.length(); i += 2)
{
title2 += title1.charAt(i);
}
You algorithm is wrong, it seems what you need to do is to extract out every nth character from source string, for example:
String source = "Book";
End result should be "Bo"
The algorithm should be:
Iterate through each character in the original string, use a stride as you need, in this case, a stride of 2 should do (so rather than increment by one, increment by the required stride)
Take the character at that index and add it to your second string
The end result should be a string which holds every nth character.
I don't really understand what you are attempting, but I can tell you what you are doing. Your loop structure does the following:
when i = 0, it compares all characters in both strings (0 + n = n, so the inner loop goes from x - title1.length()).
when i = 1, compare all characters except the first one (for size x, 1 + n = x - 1 comparisons).
when i =2, compare x / 2 characters (for size x, 2 + n = x / 2)
when i = 3, compare x / 3 characters
... and so on
System.out.print(title.charAt(0,1)) Shouldn't even compile. charAt(int) is the correct call. And if title length is greater than 0, this will always print a String with a single character -- the first one in title. And it will always be the same unless you reassign title to a different String.
Also this code will always throw an IndexOutOfBoundsException at title.charAt(i+x) when i = title.length() - 1 and x > 0.

Finding various char's within a String

I have a basic String variable that contains the letter x a total of three times.
I have attempted to find x within the String using charAt, and then print the char and the next two characters next to it.
I have hit a snag within my code and would appreciate any help.
Here is my code.
public class StringX{
public static void main(String[] args){
String ss = "xarxatxm";
char first = ss.charAt(0);
char last == ss.charAt(3);
if(first == "x"){
String findx = ss.substring(0, 2);
}
if(last == "x"){
String findX = ss.substring(3, 5);
}
System.out.print(findx + findX);
}
}
Also, is there a way to implement the for loop to cycle through the String looking for x also?
I just need some advice to see where my code is going wrong.
You cannot find characters using charAt - it's for getting a character once you know where it is.
Is there a way to implement the for loop to cycle through the String looking for x also?
You need to use indexOf for finding positions of characters. Pass the initial position which is the position of the last x that you found so far to get the subsequent position.
For example, the code below
String s = "xarxatxm";
int pos = -1;
while (true) {
pos = s.indexOf('x', pos+1);
if (pos < 0) break;
System.out.println(pos);
}
prints 0 3 6 for the three positions of 'x' in the string.

Categories