Given 3 unique letters: can you print the six possible non repeating combinations of the letters, using a recursive function. 'cat' should output: cat, act, atc, tac, tca and cta. Here's my program, I'm having trouble finding the recursive algorithm. Here's my attempt:
static void findWords(StringBuilder string, int start, int stride) {
//1. iterate through all possible combinations of the chars recursively
System.out.println(string);
if (stride < string.length() && start < string.length())
{
char temp = string.charAt(stride);
string.setCharAt(stride, string.charAt(start));
string.setCharAt(start, temp);
findWords(string, start, stride + 1);
findWords(string, start + 1, stride + 1 );
}
}
public static void main(String[] args)
{
StringBuilder word = new StringBuilder("cat");
findWords(word,0,1);
}
Solution:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
static List<String> resultList = new ArrayList<>();
static void computeResult(char[] s, int pos, String resultString) {
if (pos == 3) {
resultList.add(resultString);
return;
}
for (int i = 0; i < 3; ++i) {
if (!resultString.contains(String.valueOf(s[i]))) {
computeResult(s, pos + 1, resultString + s[i]);
}
}
}
public static void main(String... args) {
Scanner sc = new Scanner(System.in);
char[] s = sc.next().toCharArray();
sc.close();
computeResult(s, 0, "");
for(String str : resultList) {
System.out.println(str);
}
}
}
Explanation:
The recursion is done by the computeResult function. It starts with an empty string, then it iterates through all possible letters 'c', 'a' and 't', appending them to the resultString, now there are 3 strings and for each one of them the function computeResult is called again. Then it does the same thing and also adds only those letters to the resultString that haven't been added yet, so to 'c' we append 'a' resulting in 'ca' and 't', resulting in 'ct', I think the rest you can figure out yourself.
Note than this works if the letters are unique. If they are not, for example you are given the string 'tat', you can transform it into t1a1t2 and do the same procedure for the array ['t1', 'a1', 't2'], then remove the digits.
The algorithm I used is quite simple. Make each character the first character of the string and find combinations with other two characters. So for the characters c, a, t the combinations would be
c at
c ta
a ct
a tc
t ca
t ac
Code:
static void findWords(String str, int pos) {
if(str == null || pos < -1) {
return;
}
int len = str.length();
if(pos + 1 < len) {
findWords(str, pos + 1);
}
//find char swap positions
int pos1 = (pos + 1) % len;
int pos2 = (pos - 1 + len) % len;
char[] chars = str.toCharArray();
String str1 = new String(new char[] {chars[pos], chars[pos1], chars[pos2]});
String str2 = new String(new char[] {chars[pos], chars[pos2], chars[pos1]});
System.out.println(str1);
System.out.println(str2);
}
public static void main(String[] args) {
String word = new String("abc");
findWords(word, 0);
}
Here is a full working example with my comments to explain the algorithm.
This solution is based on backtracking. Read more about that here. Look at the problem as a tree. In your example the word is "cat". Here comes some ascii art...
cat
/ | \
Cat Act Tca
/ \ / \ / \
CAt CTa ACt ATc TCa TAc
At each pass, you fix a letter (I put it as a capital). The further down the tree you get the less there is that you can swap because you've fixed a certain amount of letters in place (at level 0 nothing is fixed, at level 1, one letter is fixed so a swap can be done, at level 2 you have no more swaps (the swap would be with itself), so the recursion reaches its base case.
public static void main(String[] args) {
// get all the permutations of a word with distinct letters
Set<String> permutations = getPermutations("cat");
// print the resulting set
System.out.println(permutations);
}
private static Set<String> getPermutations(String string) {
// recursive call to get a permutation of the given string and put it in
// the set of permutations (initially empty)
return permute(string, 0, string.length() - 1, new HashSet<String>());
}
private static Set<String> permute(String string, int left, int right, Set<String> set) {
if (left == right) {
// swap would be with itself so just add the string
// this is the base case
set.add(string);
} else {
for (int i = left; i <= right; i++) {
// indices are different, something can be swapped so swap it
string = swap(string, left, i);
// permute the swapped string starting from the next available character
permute(string, left + 1, right, set);
}
}
return set;
}
// utility method to carry out the swapping
// you could do this with primitive char[] and probably improve performance
// but for the sake of simplicity as it's just an exercise I used a
// StringBuilder
private static String swap(String in, int i, int j) {
char tmp1 = in.charAt(i);
char tmp2 = in.charAt(j);
StringBuilder sb = new StringBuilder(in);
// put the char at j in place of the char at i
sb.setCharAt(i, tmp2);
// and do the same the other way around
sb.setCharAt(j, tmp1);
return sb.toString();
}
Related
So, i am basically new to java ,and there was this question on our programming test
input:ww:ii:pp:rr:oo
if the alphabets are same then consider only once
output:wipro
so i was able to remove the : from the input and was also able to separate them
my current output :[w,w,i,i,p,p,r,r,o,o]
but i am unable to consider the same characters only once,its been nearly 35 min :_(
String txt="ww:ii:pp::rr:oo";
String[] result= txt.split(":");
System.out.println(Arrays.toString(result));//1
String n11="";
for(String str:result){
n11 += str;
}
System.out.println(n11);//2
result=n11.split("");
System.out.println(Arrays.toString(result));//3
String n12="";
int i=0;
for(String i:result){
if(i.equals(i+1)){
continue;
}
else {
n12=n12+i;
}
}
System.out.println(n12);//4
}
output
[ww, ii, pp, , rr, oo]
wwiipprroo
[w, w, i, i, p, p, r, r, o, o]
[nullw, nullw, nulli, nulli, nullp, nullp, nullr, nullr, nullo, nullo]
Example:
public class GFG
{
/* Method to remove duplicates in a sorted array */
static String removeDupsSorted(String str)
{
int res_ind = 1, ip_ind = 1;
// Character array for removal of duplicate characters
char arr[] = str.toCharArray();
/* In place removal of duplicate characters*/
while (ip_ind != arr.length)
{
if(arr[ip_ind] != arr[ip_ind-1])
{
arr[res_ind] = arr[ip_ind];
res_ind++;
}
ip_ind++;
}
str = new String(arr);
return str.substring(0,res_ind);
}
/* Method removes duplicate characters from the string
This function work in-place and fills null characters
in the extra space left */
static String removeDups(String str)
{
// Sort the character array
char temp[] = str.toCharArray();
//Arrays.sort(temp);
str = new String(temp);
// Remove duplicates from sorted
return removeDupsSorted(str);
}
// Driver Method
public static void main(String[] args)
{
String str = "ww:ii:pp:rr:oo";
String str1 = str.replaceAll(":","");
System.out.println(removeDups(str1));
}
}
The source is taken from www.geeksforgeeks.org And added String str1 = str.replaceAll(":","");
Output:
Your first step is right. But, you have an error in i.equals(i+1) since i + 1 isn't is the next element. You should iterate the array like this:
for (int i = 0; i < result.length - 1; i ++) {
if (result[i].equals(result[i + 1])) {
// do the remove operation.
}
}
I have written a Java program to find Anagram for 2 strings.
For Reference:
Two strings are anagrams if they are written using the same exact letters, ignoring space, punctuation and capitalization. Each letter should have the same count in both strings. For example, Army and Mary are anagram of each other.
Program:
package practice;
import java.util.ArrayList;
import java.util.List;
public class Anagram_String {
public static void main(String[] args) {
String s1="mary";
String s2="army";
int k=0;
List<String> matchedChar= new ArrayList<String>();
String charmatch="";
char[] ch1= s1.toLowerCase().toCharArray();
char[] ch2= s2.toLowerCase().toCharArray();
if(s1.length()==s2.length())
{
for(int i=0;i<s1.length();i++)
{
for(int j=0;j<s2.length();j++)
{
if(ch1[i]==ch2[j])
{
k++;
charmatch=String.valueOf(ch1[i]);
System.out.println(charmatch);
matchedChar.add(charmatch);
System.out.println("Arraylist value is "+matchedChar.toString());
System.out.println(matchedChar.size());
}
}
k=0;
}
String arrayValue=matchedChar.toString();
System.out.println("Array value is "+arrayValue);
if(arrayValue.contains(s2)){
System.out.println("String 1 and String 2 are anagrams of each other");
}
else
{
System.out.println("String 1 and String 2 are not anagrams of each other");
}
}
}
}
Output:
m
Arraylist value is [m]
1
a
Arraylist value is [m, a]
2
r
Arraylist value is [m, a, r]
3
y
Arraylist value is [m, a, r, y]
4
Array value is [m, a, r, y]
String 1 and String 2 are not anagrams of each other
Here if you see all the characters are added to to the arraylist but when compared with the string, it is showing the output as they are not anagrams of each other.
Kindly help me to find solution for this.
Thank you,
What I think is that your solution will work only for words with unique characters, and time complexity will be O(n^2) (where n - is the length of String).
However, there is a better solution for such problem:
Take String.toCharArray() value for each string
Sort those arrays
If those arrays are equal, then your words are anagrams
You can count number of letters in both strings. If both strings have the same number of letters they are anagrams.
You can use an int[] to store number of letters.
public static boolean anagrams(String a, String b) {
int[] letters = new int[26];
// Convert to upper case because the test is case insensitive
a = a.toUpperCase();
b = b.toUpperCase();
for (int i = 0; i < a.length(); i++) {
char ch = a.charAt(i);
if (ch < 'A' || ch > 'Z') {
continue; // Skip non letters
}
letters[ch - 'A']++; // Increment number of the current letter
}
for (int i = 0; i < b.length(); i++) {
char ch = b.charAt(i);
if (ch < 'A' || ch > 'Z') {
continue; // Skip non letters
}
letters[ch - 'A']--; // Decrement number of the current letter
}
for (int i = 0; i < letters.length; i++) {
if (letters[i] != 0) {
// If there are more or less of this letter
// in the first string it is not an anagram
return false;
}
}
return true;
}
Note this algorithm is done in O(n) where n is the number of letters of each string. Sorting the strings needs at least O(n log(n))
Taking the idea from AxelH's comments it is possible to create an external method to loop as follow.
private void countLetters(int[] letters, String str, int incrementFactor) {
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if (ch < 'A' || ch > 'Z') {
continue; // Skip non letters
}
letters[ch - 'A'] += incrementFactor;
}
}
public static boolean anagrams(String a, String b) {
int[] letters = new int[26];
countLetters(letters, a.toUpperCase(), 1); // Note the +1
countLetters(letters, b.toUpperCase(), -1); // Note the -1
for (int i = 0; i < letters.length; i++) {
if (letters[i] != 0) {
// If there are more or less of this letter
// in the first string it is not an anagram
return false;
}
}
return true;
}
This seems to me a more readable and elegant way. Thanks AxelH.
Note In the previous code there are expressions like letters[ch - 'A']++. This line of code use an interesting properties of type char of java that is a special primitive numeric type, so it is possible to use mathematical operations on it.
In particular:
'A' - 'A' --> 0
'B' - 'A' --> 1
'C' - 'A' --> 2
'D' - 'A' --> 3
...
'Z' - 'A' --> 25
So this expression can be used to give an index to a letter starting from 0 for A ending to 25 for Z.
My answer is quite similar to Marine's, but takes a little higher-level approach with Java 8 streams, making the code a little more concise and readable:
public class Application {
public boolean isAnagramsEqual(String str1, String str2) {
Map<Character, Long> count = countChars(str1);
Map<Character, Long> count2 = countChars(str2);
return count.equals(count2);
}
private Map<Character, Long> countChars(String str) {
return str.toLowerCase()
.chars().mapToObj(ch -> (char) ch) //convert into stream of Characters
.filter(Character::isLetterOrDigit) //filter out not-needed chars
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}}
Method countChars creates a map with each unique character mapped to it's count in the given string.
It may be a little less performant than Marine's, but it's still O(n).
Your outputs says it itself:
Array value is [m, a, r, y]
As mentioned above I would also just create array and sort them, but here is the solution you may be searching for:
String s1="mary";
String s2="army";
List<String> matchedChar= new ArrayList<String>();
String charmatch="";
char[] ch1= s1.toLowerCase().toCharArray();
char[] ch2= s2.toLowerCase().toCharArray();
if(s1.length()==s2.length())
{
for(int i=0;i<s1.length();i++)
{
for(int j=0;j<s2.length();j++)
{
if(ch1[i]==ch2[j])
{
charmatch=String.valueOf(ch1[i]);
System.out.println(charmatch);
matchedChar.add(charmatch);
System.out.println("Arraylist value is "+matchedChar.toString());
System.out.println(matchedChar.size());
}
}
}
String arrayValue="";
for (String s : matchedChar){
arrayValue = arrayValue + s;
}
System.out.println("Array value is "+arrayValue);
System.out.println("s1 value is "+s1);
if(arrayValue.equals(s1)){
System.out.println("String 1 and String 2 are anagrams of each other");
}
else
{
System.out.println("String 1 and String 2 are not anagrams of each other");
}
}
Use .split("(?!^)") on your String to make it an String Array. Next sort arrays and compare it. It's the best option for me too.
This is how you can do it by sorting the arrays:
public static void main(String[] args) {
System.out.println(isAnagram("mary", "army"));
}
public static boolean isAnagram(String s1, String s2){
char[] c1 = s1.toLowerCase().toCharArray();
char[] c2 = s2.toLowerCase().toCharArray();
Arrays.sort(c1);
Arrays.sort(c2);
boolean anagram = false;
if(Arrays.equals(c1, c2)){anagram = true;}
return anagram;
}
In this code i converted my string into char array using the code :String.toCharArray() inside a function named toSort() and sorted the words in the string. Then inside Isanagram() method I checked whether it is anagram or not. For that first I have to make sure whether the sorted strings are of same length or not after I compared each character in one string with other.
Here is the full code try to decompose each method and study.
import java.util.Scanner;
public class StANaEx1 {
String toSort(String s5){
int i,j;
char temp;
char ch1[] = s5.toCharArray();
int len = ch1.length;
for(i=0;i<len;i++){
for(j=i+1;j<len;j++){
if(ch1[i]>ch1[j]){
temp = ch1[i];
ch1[i] = ch1[j];
ch1[j] = temp;
}
}
}
String s6 = new String(ch1);
return s6;
}
void isAnagram(String s9,String s10){
int i,len1,len2,flag=0;
System.out.println("s9 : "+s9);
System.out.println("s10 : "+s10);
System.out.println("");
s9 = s9.trim(); //To remove white spaces again no need.I used because my compiler didn't recognize my replace() method in main() method.
s10 = s10.trim();
len1 = s9.length();
len2 = s10.length();
System.out.println("len1 : "+len1); //To check the length of the given strings without white spaces.
System.out.println("len2 : "+len2);
System.out.println("");
for(i=0;i<len1;i++){
System.out.println("s9["+i+"] : "+s9.charAt(i)); //Error checking.
}
System.out.println("");
for(i=0;i<len2;i++){
System.out.println("s10["+i+"] : "+s10.charAt(i));
}
System.out.println("");
if(len1!=len2){
System.out.println("Not Anagram string length different");
}
else{
for(i=0;i<len1;i++){ //Since string lengths are same len1 = len2.
if(s9.charAt(i)!=s10.charAt(i)){
flag=1;
break;
}
}
if(flag==0){
System.out.println("Anagram");
}
else{
System.out.println("Not Anagram");
}
}
}
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
StANaEx1 ob1 = new StANaEx1();
System.out.println("-----Anagram checking-----");
System.out.println("");
System.out.println("Enter the 1st String: ");
System.out.println("");
String s1 = sc.nextLine();
s1 = s1.replace("//s", ""); //This is to remove white spaces.
String s3 = s1.toLowerCase(); //Change the input string to lower case in order to make sorting easy.
System.out.println("");
System.out.println("Enter the next String: ");
String s2 = sc.nextLine();
s2 = s2.replace("//s", "");
String s4 = s2.toLowerCase();
System.out.println("");
String s7 = ob1.toSort(s3);
String s8 = ob1.toSort(s4);
ob1.isAnagram(s7,s8);
sc.close();
}
}
Output
-----Anagram checking-----
Enter the 1st String:
Mary
Enter the next String:
army
s9 : amry
s10 : amry
len1 : 4
len2 : 4
s9[0] : a
s9[1] : m
s9[2] : r
s9[3] : y
s10[0] : a
s10[1] : m
s10[2] : r
s10[3] : y
Anagram
Output 2
-----Anagram checking-----
Enter the 1st String:
Sniper
Enter the next String:
kill
s9 : einprs
s10 : ikll
len1 : 6
len2 : 4
s9[0] : e
s9[1] : i
s9[2] : n
s9[3] : p
s9[4] : r
s9[5] : s
s10[0] : i
s10[1] : k
s10[2] : l
s10[3] : l
Not Anagram string length different
import java.util.Arrays;
public class AnagramString {
public static void main(String[] args) {
String str1="Keep";
String str2="peek";
//convert the string into the array with lower casing its character
char arrstr1[]=str1.toLowerCase().toCharArray();
char arrstr2[]=str2.toLowerCase().toCharArray();
//sort the array of character by acending order
Arrays.sort(arrstr1);
Arrays.sort(arrstr2);
//set true boolean value to the status
boolean status=true;
//comparing the char array
status = Arrays.equals(arrstr1, arrstr2);//here we get true value if they are containing the same character
System.out.println(Arrays.toString(arrstr1));
System.out.println(Arrays.toString(arrstr2));
if(arrstr1.length==arrstr2.length && status) {
System.out.println("2 string are anagram");
}else {
System.out.println("2 string are not anagram");
}
}
}
I want to write some automated tests for web app authentication. The login password is case-sensitive and always contains at least one alphabetic character.
I want to write a test where I randomly change the case of one or more alphabetic characters.
Let's say the password string is "123te123st!".
Now I want to change this string to one which contains at least one uppercase letter. I'm trying to make sure that the login is still case-insensitive and any variation in case will fail to match the password.
Does anybody know a elegant way to do it? I searched already (including Apache Commons) but couldn't find a helper method.
You can look at the randomAlphaNumeric from the RandomStringUtils, although it would seem that you are not guaranteed for it to have an upper case. To go around this, you could get the first lowercase letter and use the .toUpper() method to get it to upper case.
Alternatively, you could generate random numbers between 0 and 9 and 65 and 90 and 97 and 122. The first set should get you random numbers, you could then cast the second number to a character to get your upper case letter(s) and do the same on the last number to get your lower case ones.
That being said, when testing one usually goes for data which is predetermined rather than generating data on the fly, since that would make it easier to debug. Having a simple pool of passwords might also be easier to implement and would also allow you to better test edge cases.
class Case
{
public static void main(String ar[])
{
String s = "upperCase",split[];
split = s.split("");
int len = s.length(),i=0;
while(i!=len)
{
if(split[i].toUpperCase() == split[i])
{
System.out.println("Password Contains One UpperCase Latter");
break;
}
i++;
}
}
}
By using this code u can easily check whether string contain uppercase or not.
if output prints "Password Contains One Uppercase Latter" this message then string contain at least on uppercase.
In this case output would like:
You can use this class to generate random passwords with a constraint on uppercase letters.
import java.util.Random;
public class RandomPasswordGenerator {
private static final String ALPHA_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String ALPHA = "abcdefghijklmnopqrstuvwxyz";
private static final String NUM = "0123456789";
private static final String SPL_CHARS = "!##$%^&*_=+-/";
public static char[] generatePswd(int minLen, int maxLen, int noOfCAPSAlpha,
int noOfDigits, int noOfSplChars) {
if(minLen > maxLen)
throw new IllegalArgumentException("Min. Length > Max. Length!");
if( (noOfCAPSAlpha + noOfDigits + noOfSplChars) > minLen )
throw new IllegalArgumentException
("Min. Length should be atleast sum of (CAPS, DIGITS, SPL CHARS) Length!");
Random rnd = new Random();
int len = rnd.nextInt(maxLen - minLen + 1) + minLen;
char[] pswd = new char[len];
int index = 0;
for (int i = 0; i < noOfCAPSAlpha; i++) {
index = getNextIndex(rnd, len, pswd);
pswd[index] = ALPHA_CAPS.charAt(rnd.nextInt(ALPHA_CAPS.length()));
}
for (int i = 0; i < noOfDigits; i++) {
index = getNextIndex(rnd, len, pswd);
pswd[index] = NUM.charAt(rnd.nextInt(NUM.length()));
}
for (int i = 0; i < noOfSplChars; i++) {
index = getNextIndex(rnd, len, pswd);
pswd[index] = SPL_CHARS.charAt(rnd.nextInt(SPL_CHARS.length()));
}
for(int i = 0; i < len; i++) {
if(pswd[i] == 0) {
pswd[i] = ALPHA.charAt(rnd.nextInt(ALPHA.length()));
}
}
return pswd;
}
private static int getNextIndex(Random rnd, int len, char[] pswd) {
int index = rnd.nextInt(len);
while(pswd[index = rnd.nextInt(len)] != 0);
return index;
}
}
You can try like this:
public class Test{
public static void main(String[] args){
String s = "1a23test12hjsd2"; // Take it as a password
char[] c= s.toCharArray(); //Convert string in chararray
boolean flag= false;
StringBuilder s1= new StringBuilder();
for(int d:c){
if(d>=97 && d<=122 && !flag){ //Converting lowercase to upper case
d=d-32;
flag=true;
}
s1.append((char)d);
}
System.out.println(s1);
}
}
To generate all capitalized variants of a string, it makes sense to scan the string and store the position of each letter in a list. This will let you iterate over the letters while skipping the non-letter characters.
For example, for the string "_a_b_c_", you want to store the positions [1, 3, 5].
Next, make a boolean array of the same length as the list of letter positions. This will represent the positions of letters that have had their case inverted.
To generate the next capitalized variant, pretend that the boolean array represents a binary number in reverse. Add 1 to that boolean number, which means scanning the array from the beginning, flipping each true to false until you reach a false, which you flip to true. As you flip each bit, invert the case of the corresponding character in the string.
Thus, we get the following 23 - 1 = 7 variants of "_a_b_c_":
binary number reversed capitalized variant
001 100 _A_b_c_
010 010 _a_B_c_
011 110 _A_B_c_
100 001 _a_b_C_
101 101 _A_b_C_
110 011 _a_B_C_
111 111 _A_B_C_
Here is a complete Java implementation.
import java.util.*;
import java.io.*;
public class VaryCaps {
int wordLength,
numLetters;
Integer letterPositions[];
boolean inverted[];
StringBuffer buffer;
public VaryCaps(String word) {
wordLength = word.length();
List<Integer> positionList = new ArrayList<Integer>();
for (int i = 0; i < wordLength; ++i) {
if (Character.isLetter(word.charAt(i))) {
positionList.add(i);
}
}
numLetters = positionList.size();
letterPositions = positionList.toArray(new Integer[numLetters]);
inverted = new boolean[numLetters];
buffer = new StringBuffer(word);
}
private void invert(int index) {
int pos = letterPositions[index];
char ch = buffer.charAt(pos);
if (Character.isUpperCase(ch)) {
ch = Character.toLowerCase(ch);
} else {
ch = Character.toUpperCase(ch);
}
buffer.setCharAt(pos, ch);
inverted[index] = !inverted[index];
}
public String next() {
int index = 0;
while (index < numLetters && inverted[index]) {
invert(index++);
}
if (index == numLetters) {
return null;
}
invert(index);
return buffer.toString();
}
public static void main(String[] args) {
VaryCaps rc = new VaryCaps("_a_b_c_");
String s;
while ((s = rc.next()) != null) {
System.out.println(s);
}
}
}
As the title implies, I'm having difficulty trying to recursively determine all the permutations of a given String. The catch is that String has to be given through a constructor of an object and then each of the permutations be found one by one. Basically, it has to work like this:
PermutationIterator iter = new PermutationIterator("eat");
while (iter.hasMorePermutations())
System.out.println(iter.nextPermutation());
Here is the code that I'm using but doesn't seem to work and I don't know how to fix it.
public class PermutationIterator {
private String word;
private int pos;
private PermutationIterator tailIterator;
private String currentLetter;
public PermutationIterator(String string) {
word = string;
pos = 0;
currentLetter = string.charAt(pos) + "";
if (string.length() > 1)
tailIterator = new PermutationIterator(string.substring(pos + 1));
}
public String nextPermutation() {
if (word.length() == 1) {
pos++;
return word;
} else if (tailIterator.hasMorePermutations()) {
return currentLetter + tailIterator.nextPermutation();
} else {
pos++;
currentLetter = word.charAt(pos) + "";
String tailString = word.substring(0, pos) + word.substring(pos + 1);
tailIterator = new PermutationIterator(tailString);
return currentLetter + tailIterator.nextPermutation();
}
}
public boolean hasMorePermutations() {
return pos <= word.length() - 1;
}
}
Right now the program prints "eat" and "eta" but after that it through a StringIndexOutOfBounds error off of the second stack. Any help with solving this is much appreciated.
Rather than just supplying the fix let me help diagnose your issue and then you can have a go at fixing it.
If you look carefully at your code you'll see that the hasMorePermutations condition passes when pos == word.length() - 1. That means nextPermutation will be run when pos is pointing to the last character in the string. But in that case when the third branch executes you increment pos and then call word.substring(pos + 1). At that point pos + 1 will be larger than length of the string which will throw the exception.
I expect the fix will be fairly easy.
try this code - generates permutations for any given string
package testing;
import java.util.ArrayList;
import java.util.List;
public class Permutations {
/*
* You will get n! (factorial) - permutations from this
*
* Just like this Example: abc (3! = 6 permutations) [abc acb bac bca cab
* cbc]
*/
static String str = "abcd";
static char[] ch = str.toCharArray();
static List<String> s1 = new ArrayList<>();
static List<String> s2 = new ArrayList<>();
public static void main(String[] args) {
// s1 - list stores initial character from the string
s1.add(String.valueOf(ch[0]));
// recursive loop - char by char
for (int k = 1; k < ch.length; k++) {
// adds char at index 0 for all elements of previous iteration
appendBefore(s1, ch[k]);
// adds char at last index for all elements of previous iteration
appendAfter(s1, ch[k]);
// adds char middle positins like a^b^C - if prev list stores
// elements
// whose size() is 3 - then it would have 2 positions fill
/*
* say d is next char - d should be filled in _^_^_ _ positions are
* previous permuions for 3 chars a,b,c(i.e 6 permutations
*/
appendMiddle(s1, ch[k], k);
// for every iteration first clear s1 - to copy s2, which contains
// previous permutatons
s1.clear();
// now copy s2 to s1- then clear s2
// - this way finally s2 contains all the permutations
for (int x = 0; x < s2.size(); x++) {
s1.add(s2.get(x));
}
System.out.println(s1);
System.out.println(s1.size());
s2.clear();
}
}
private static void appendMiddle(List str, char ch, int positions) {
for (int pos = 1; pos <= positions - 1; pos++) {
for (int i = 0; i < str.size(); i++) {
s2.add(str.get(i).toString().substring(0, pos) + String.valueOf(ch)
+ str.get(i).toString().substring(pos, str.get(i).toString().length()));
}
}
}
private static void appendBefore(List str, char ch) {
for (int i = 0; i < str.size(); i++) {
s2.add(String.valueOf(ch) + str.get(i));
}
}
private static void appendAfter(List str, char ch) {
for (int i = 0; i < str.size(); i++) {
s2.add(str.get(i) + String.valueOf(ch));
}
}
}
do a little change in your hasMorePermutation method as below to solved StringIndexOutOfBounds exception.
public boolean hasMorePermutations()
{
return pos < word.length() - 1;
}
import java.util.Scanner;
/**
*
* #author Cutuk
*/
public class JavaApplication3 {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
String a;
Scanner in = new Scanner (System.in);
a = in.nextLine();
char first = a.charAt(0);
System.out.print(first);
int v= a.length()-1;
char last = a.charAt(v);
int k= a.length();
int random=0;
char x='\u0000';
char middle= '\u0000' ;
for (int i=1; i<a.length()-1;i++){
random= (int )(Math.random() * (k-2) + 1);
middle=a.charAt(random);
x=middle;
System.out.print(x);
}
System.out.print(last);
}
}
I am supposed to take a word, shuffle the letters inside, but keep the first and the last letter unchanged. I managed to randomize, but I cannot keep it from repeating.
Your approach is incorrect: when you pick middle letters at random, it is impossible to guarantee that all letters from the middle of the word would be printed (and as a consequence, that other letters would not be repeated).
There are several ways of fixing this:
Each time you generate a random index, mark that index in an array of booleans. The length of the array is equal to the length of the word. Check the array before using each new index that you generate; if the index is marked, continue generating new random indexes until you hit an empty one.
Create an array of integer indexes of letters inside the word (i.e. 1 through length-1, inclusive). Do a random shuffle on the array, and use the shuffled indexes to pick middle letters.
Similar to 2, except you put all middle letters in an array, and shuffle it.
If I understand your question, I would suggest you start by building a List<Character> and then use Collections.shuffle(List) and finally build your return String with a StringBuilder like
private static String shuffleLetters(String in) {
if (in == null || in.length() < 3) {
return in;
}
char first = in.charAt(0);
char last = in.charAt(in.length() - 1);
List<Character> chars = new ArrayList<>();
for (char ch : in.substring(1, in.length() - 1).toCharArray()) {
chars.add(ch);
}
Collections.shuffle(chars);
StringBuilder sb = new StringBuilder();
sb.append(first);
for (char ch : chars) {
sb.append(ch);
}
sb.append(last);
return sb.toString();
}
Assuming "shuffling" can allow a middle character to sometimes be swapped with itself, you can do something like:
private static final String[] TEST_WORDS = {"apple", "banana", "pear", "raspberry", "cucumber", "watermelon", "a", "ab", "", null};
public static void main(String[] args)
{
for (String word: TEST_WORDS)
{
System.out.println(shuffleInside(word));
}
}
private static String shuffleInside(String word)
{
String ret = word;
if (word != null)
{
Random r = new Random();
int strLen = word.length();
if (strLen > 2)
{
char[] middleChars = word.substring(1, strLen - 1).toCharArray();
shuffle(middleChars, r);
ret = word.charAt(0) + new String(middleChars) + word.charAt(strLen - 1);
}
}
return ret;
}
private static void shuffle(char[] chars, Random r)
{
for (int i = chars.length - 1; i > 0; i--)
{
int index = r.nextInt(i + 1);
char c = chars[index];
chars[index] = chars[i];
chars[i] = c;
}
}
Which handles the case where the word is null, one character, two characters, or two or more characters and produces the following output on a single run:
apple
bnaana
paer
rerrsbpay
cbuemucr
waomteerln
a
ab
null
You could simply create a List<Integer> for storing the random numbers that you generated.
Here is your code from above, cleaned up, with meaningful naming and the List for looking up the history:
public class Main {
public static void main(String[] args) {
final Scanner in = new Scanner(System.in);
final Random rnd = new Random(System.currentTimeMillis());
final List<Integer> rndHistory = new LinkedList<>(); // <-- your history
System.out.print("Please type a word: ");
final String input = in.nextLine();
System.out.print(input.charAt(0));
for (int i = 1, random = 0; i < input.length() - 1; i++) {
do {
random = rnd.nextInt(input.length() - 2) + 1;
} while (rndHistory.contains(random)); // check the history
rndHistory.add(random); // add to the history
System.out.print(input.charAt(random));
}
System.out.println(input.charAt(input.length() - 1));
}
}
Main differences:
final Random rnd = new Random(System.currentTimeMillis()); Using
the the java.util.Random class is a better way for generating
random numbers.
final List<Integer> rndHistory = new LinkedList<>(); This is the actual difference, part of the mechanism to prevent double shuffles
System.out.print("Please type a word: "); a meaningful prompt for
the user (who is going to know what to do, when you program is
executed and there is nothing on the screen?)
and finally:
do {
random = rnd.nextInt(input.length() - 2) + 1;
} while (rndHistory.contains(random)); // check the history
rndHistory.add(random); // add to the history
The 'prevent a random number from being used twice' mechanics
For your random try this: Random generator = new Random(System.currentTimeMillis());