Given a string, I want to find all variants without transposition, only deletion. For example, given the string:
helloo
The list of variants would be as follows (separated by white space).
helloo hello heloo helo
My solution so far is to move through each character, and then if the current character matches the next character, recursively try the original and the deleted character version, as follows.
// takes String with at most two consecutive characters of any character,
// and returns an Iterable of all possible variants (e.g. hheello -> heello, hhello, ...)
private static Iterable<String> findAllVariants(String word) {
StringBuilder variant = new StringBuilder(word);
Queue<String> q = new LinkedList<String>();
findAllVariants(word, variant, 0, q);
return q;
}
// helper method
private static void findAllVariants(String word, StringBuilder variant, int currIndex, Queue<String> q) {
if (currIndex == variant.length() - 1) q.add(variant.toString());
for (int i = currIndex; i < variant.length() - 1; i++) {
char thisChar = variant.charAt(i);
char nextChar = variant.charAt(i+1);
if (thisChar == nextChar) {
// get all variants with repeat character
findAllVariants(word, variant, i+1, q);
// get all variants without repeat character;
variant = variant.deleteCharAt(i);
findAllVariants(word, variant, i, q);
}
}
}
However, I end up getting a large number of copies of answers, and none of others. When I do my algorithm on paper, it seems correct. What am I doing wrong?
Something along the lines of the following code will enable you to get all possibilities (remember to add word itself if needed). The idea is to retreive all possibilities for removing one char (e.g. hello results in ello hllo helo hell). These results can in turn be used to get the possibilities for removing two chars (remove one char again). Resulting in llo elo ell for ello and so on...
List<String> getPossibilities(String word) {
int removeChars = word.length() - 1;
List<String> possibilities = new ArrayList();
List<String> options = Arrays.asList(word);
for(int i = 0; i <= removeChars; i++) {
List<String> results = new ArrayList();
for(String option : options) {
for(String result : removeOneChar(option)) {
if(!results.contains(result)) {
results.add(result);
}
}
}
possibilities.addAll(results);
options = results;
}
return possibilities;
}
private static List<String> removeOneChar(String word) {
List<String> results = new ArrayList();
for(int i = 0; i < word.length(); i++) {
int secondPart = i + 2;
if(secondPart <= word.length()) {
results.add(
word.substring(0, i)
+ word.substring(i + 1, word.length()));
}
else {
results.add(
word.substring(0, i));
}
}
return results;
}
Notice the if(!contains(result)) in order to prevent any duplicates.
Note I've used substring() to accomplish this, you're approach with removeCharAt() is a another good option. You could run some tests to see which performs better to decide which one to use. Notice using the latter could possibly remove the need of the if in the private method.
I'd use rather different algorithm: I'd find all repetitions (ll) (oo) (lll) (ooo) etc.., keep an array describing their positions in the text, and the count of characters per each repetition.
e.g Array A =
[l|2]
[o|2]
.
.
.
Then I'd say have second array with initial count zero and increase there the count and print out all permutations:
Array B =
[l|1]
[o|1]
==> prints helo
Step 2: (increment count)
B =
[l|2]
[o|1]
==> prints hello
Step 3:
B =
[l|3] ==> bigger than max,so reset it to 0, and increment the second cell now, so it becomes:
B =
[l|1]
[o|2]
==> prints heloo
Step 4: (increment first elem again)
[l|2] ==> not bigger than max, so no overflow, so keeping it that way
[o|2]
==> prints helloo
Related
How to reverse the words in a sentence, but not punctuation using recursion. The sentence is said to use punctuation marks: ,.?!
Input: "Jack, come home!"
Output: "home, come Jack!"
Now I have somehow managed to complete the task correctly but without using recursion.
How should I convert this work to use recursion to solve the problem?
Here's the method:
public static StringBuilder reverseSentenceWithPunctuation(String sentence, int i) {
String[] parts = sentence.split(" ");
StringBuilder newSentence = new StringBuilder();
Map<Integer, Character> punctuationMap = new HashMap<>();
for (int j = 0; j < parts.length; j++) {
if (parts[j].endsWith(",") || parts[j].endsWith(".") || parts[j].endsWith("!") || parts[j].endsWith("?")) {
char lastSymbol = parts[j].charAt(parts[j].length()-1);
punctuationMap.put(j, lastSymbol);
String changedWord = parts[j].replace(String.valueOf(lastSymbol), "");
parts[j] = changedWord;
}
}
for (int j = parts.length-1; j >= 0; j--) {
newSentence.append(parts[j]);
if (punctuationMap.containsKey(i)) {
newSentence.append(punctuationMap.get(i));
newSentence.append(" ");
} else
newSentence.append(" ");
i++;
}
return newSentence;
}
Thanks in advance!
To implement this task using recursion, a pattern matching the first and the last words followed by some delimiters should be prepared:
word1 del1 word2 del2 .... wordLast delLast
In case of matching the input the result is calculated as:
wordLast del1 REVERT(middle_part) + word1 delLast
Example implementation may be as follows (the words are considered to contain English letters and apostrophe ' for contractions):
static Pattern SENTENCE = Pattern.compile("^([A-Za-z']+)([^A-Za-z]+)?(.*)([^'A-Za-z]+)([A-Za-z']+)([^'A-Za-z]+)?$");
public static String revertSentence(String sentence) {
Matcher m = SENTENCE.matcher(sentence);
if (m.matches()) {
return m.group(5) + (m.group(2) == null ? "" : m.group(2))
+ revertSentence(m.group(3) + m.group(4)) // middle part
+ m.group(1) + (m.group(6) == null ? "" : m.group(6));
}
return sentence;
}
Tests:
System.out.println(revertSentence("Jack, come home!"));
System.out.println(revertSentence("Jack, come home please!!"));
System.out.println(revertSentence("Jane cried: Will you come home Jack, please, don't go!"));
Output:
home, come Jack!
please, home come Jack!!
go don't: please Jack home come you, Will, cried Jane!
I don't think this is a good case for a recursive function, mainly because you need 2 loops. Also, in general, iterative algorithms are better performance-wise and won't throw a stackoverflow exception.
So I think the main reasons to work with recursive functions is readability and easiness, and honestly, in this case, I think it isn't worth it.
In any case, this is my attempt to convert your code to a recursive function. As stated before, I use 2 functions because of the 2 loops. I'm sure there is a way to achieve this with a single function that first loads the map of punctuations and then compose the final String, but to be honest that would be quite ugly.
import java.util.*;
import java.util.stream.*;
public class HelloWorld{
static Character[] punctuationCharacters = {',','.','!'};
public static void main(String []args){
System.out.println(reverseSentenceWithPunctuation("Jack, come home!"));
}
private static String reverseSentenceWithPunctuation(String sentence) {
String[] parts = sentence.split(" ");
return generate(0, parts, extractPunctuationMap(0, parts));
}
private static Map<Integer, Character> extractPunctuationMap(int index, String[] parts){
Map<Integer, Character> map = new HashMap<>();
if (index >= parts.length) {
return map;
}
char lastSymbol = parts[index].charAt(parts[index].length() - 1);
if (Arrays.stream(punctuationCharacters).anyMatch(character -> character == lastSymbol)) {
parts[index] = parts[index].substring(0, parts[index].length() - 1);
map = Stream.of(new Object[][] {
{ index, lastSymbol}
}).collect(Collectors.toMap(data -> (Integer) data[0], data -> (Character) data[1]));
}
map.putAll(extractPunctuationMap(index + 1, parts));
return map;
}
private static String generate(int index, String[] parts, Map<Integer, Character> punctuationMap) {
if (index >= parts.length) {
return "";
}
String part = index == 0? " " + parts[index] : parts[index];
if (punctuationMap.containsKey(parts.length -1 - index)) {
part += punctuationMap.get(parts.length -1 - index);
}
return generate(index + 1, parts, punctuationMap) + part;
}
}
In pseudocode maybe something like that:
take the whole sentence
(a). get the first word
(b). get the last word
(if there is a punctuation after the first or last word, leave it there)
swap(a, b) and return the remaining middle of the sentence
repeat (1) and (2) until there is only two words or one
return the last two (swapped) words left (if one word, just return that)
I'm working on an a problem trying to generate all the possible combination between two chars based on early generated combine using java 8
for example :
private static final String LETTER_RANGE = "abcdefghijklmnopqrstuvwxz";
from this letter rang I want to extract all differents combine between two chars XX
for example :
zz,zx,zw....za
xz,xx,xw....xa
..,..,..,..,..
az,ax,aw... aa
My problem is I need to generate those combination at runtime based on previous combine :
String value = generate("zx") // this should return 'zw'
Can any one helpe me on any idea how can use java 8 loops,Stream,String to do this Thanks in advance
You can use simple character arithmetics. As chars can be incremented and compared:
final List<String> permutations = new ArrayList<>(26 * 26);
for (char[] array = {'a', 'a'}; array[0] <= 'z'; array[0]++) {
for (array[1] = 'a'; array[1] <= 'z'; array[1]++) {
permutations.add(new String(array));
}
}
This piece of code creates every combination of all characters between a and z inclusive and adds them to a List.
This is possible because in ASCII the character value of a (97) is smaller than the one from z (122).
I've also used some optimizations, like the use of an array inside the for-loop to hold the current combination of 2 chars. This array can then also be directly used to create a new string, with the string constructor: String(char[]).
Tools one might use are:
char ch = LETTER_RANGE.charAt(2); // 'c'
int ix = LETTER_RANGE.indexOf(ch); // 2
Using the single char[] array is probably much easier, so the method might look like:
String next(String combination) {
char[] chars = combination.toCharArray();
char ch = chars[1];
if (...) {
}
return new String(chars);
}
Sounds like very bad solution of task for me. But if you really need it, it can be done like this.
private static final String LETTER_RANGE = "abcdefghijklmnopqrstuvwxz";
public String findNext(String prev) {
if(prev==null || prev.length()<2)
throw new RuntimeException("Invalid argument");
int char1Index = LETTER_RANGE.indexOf(prev.charAt(0));
int char2Index = LETTER_RANGE.indexOf(prev.charAt(1));
char2Index--;
if (char2Index < 0) {
char1Index--;
char2Index = LETTER_RANGE.length() - 1;
}
if (char1Index < 0) {
return null;// or what you need here.
}
return new String(new char[]{LETTER_RANGE.charAt(char1Index), LETTER_RANGE.charAt(char2Index)});
}
And the task find all Concatenation between two chars from predefined list I would do like this
public List findAll() {
List<String> result=new ArrayList<>();
char[] chars=LETTER_RANGE.toCharArray();
for(int i=0;i<chars.length;i++)
for(int j=0;j<chars.length;j++)
result.add(new String(new char[]{chars[i],chars[j]}));
return result;
}
The pattern in your example reminds me of Excel columns. Excel names its columns with letters from A to Z, and then the sequence goes AA, AB, AC... AZ, BA, BB, etc. So if we interpret your combinations as Excel column titles the task could be reworded to:
Given a column title as appears in an Excel sheet, find the next
column title (or previous as shown in your expected output).
To do this you can write a method that accepts a string as a parameter (like "zf") and returns the actual column number. And then add or substract 1 to get the number of the next or previos column and convert the number back to string. Example:
public final class ExcelColumn {
public static void main(String[] args) {
String str = "zx";
System.out.println(getPreviousColumn(str));
}
public static int toColumnNumber(String column) {
int result = 0;
for (int i = 0; i < column.length(); i++) {
result *= 26;
result += column.charAt(i) - 'a' + 1;
}
return result;
}
public static String toColumnName(int number) {
final StringBuilder sb = new StringBuilder();
int num = number - 1;
while (num >= 0) {
int numChar = (num % 26) + 97;
sb.append((char)numChar);
num = (num / 26) - 1;
}
return sb.reverse().toString();
}
public static String getNextColumn(String s) {
return toColumnName( toColumnNumber(s)+1);
}
public static String getPreviousColumn(String s) {
return toColumnName( toColumnNumber(s)-1);
}
}
ToDo:
Input validiation and
exception handling
Pros:
You can use this even if your combined string length is > 2
can be easily modified to use with uppercase letters
You can do something like from 'be' to 'cf' to generate all combinations which fall in this range if necessary
Cons:
May be to much code for a simple task. Look at #Andrii Vdovychenko's
comment which solves the problem in few lines
I'm having performance issues. Does anyone have a faster/better solution for doing the following:
String main = "";
for (String proposition : propositions) {
if (main.length() == 0) {
main = proposition;
} else {
main = "|(" + proposition + "," + main + ")";
}
}
I know concat and stringbuilder are faster, but i don't see how i can use these methods. Because of the following line of code:
main = "|(" + proposition + "," + main + ")";
Thanks in advance!
So from what I can tell there are 3 problems here:
Values are primarily prepended to the string.
For each value a character is appended.
If only one value is present, nothing should be appended or prepended.
With 2 or more items, the 0th item is handled differently:
0:""
1:"A"
2:"|(B,A)"
3:"|(C,|(B,A))"
It can be made quicker by making a few changes:
Reverse the algorithm, this means the majority of the work involves appending, allowing you to use StringBuilders.
Count the number of closing )'s and append those after the loop is finished.
Special case for 0 or 1 items in the list.
With those changes the algorithm should be able to use a StringBuilder and be a lot quicker.
Attempt at an algorithm:
int length = propositions.size();
if (length == 0) {
main = "";
} else {
StringBuilder sb = new StringBuilder();
int nestingDepth = 0;
// Reverse loop, ignoring 0th element due to special case
for (int i = length - 1; i > 0; i--) {
sb.append("|(").append(propositions.get(i)).append(',');
nestingDepth++;
}
// Append last element due to special casing
sb.append(propositions.get(0));
for (int i = 0; i < nestingDepth; i++) {
sb.append(')');
}
main = sb.toString();
}
I believe this should produce the correct results, but it should give the right idea.
The problem is that you're prepending and appending to the string as you go. String and StringBuilder dont handle this well (and give quadratic performance). But you can use a dequeue which supports insertion at start and end to store all the pieces. Then finally you can join the bits in the dequeue.
ArrayDeque bits = new ArrayDeque();
for (String proposition : propositions) {
if (bits.size() == 0) {
bits.push(proposition);
} else {
// Add prefix
main.offerFirst("|(" + proposition + "," );
// Add suffix
main.push(")");
}
}
StringBuilder sb = new StringBuilder();
for( String s : bits) {
sb.append(s);
}
main = sb.toString();
Assuming this is an array of propositions, you could first sum the length of the String(s) in the array. Add 4 for your additional characters, and subtract 4 because you don't use those separators on the first element. That should be the perfect size for your output (this is optional, because StringBuilder is dynamically sized). Next, construct a StringBuilder. Add the first element. All subsequent elements follow the same pattern, so the loop is simplified with a traditional for. Something like,
int len = Stream.of(propositions).mapToInt(s -> s.length() + 4).sum() - 4;
StringBuilder sb = new StringBuilder(len); // <-- len is optional
sb.append(propositions[0]);
for (int i = 1; i < propositions.length; i++) {
sb.insert(0, ",").insert(0, propositions[i]).insert(0, "|(").append(")");
}
System.out.println(sb);
I want to achieve something like this.
String str = "This is just a sample string";
List<String> strChunks = splitString(str,8);
and strChunks should should be like:
"This is ","just a ","sample ","string."
Please note that string like "sample " have only 7 characters as with 8 characters it will be "sample s" which will break down my next word "string".
Also we can go with the assumption that a word will never be larger than second argument of method (which is 8 in example) because in my use case second argument is always static with value 32000.
The obvious approach that I can think of is looping thru the given string, breaking the string after 8 chars and than searching the next white space from the end. And then repeating same thing again for remaining string.
Is there any more elegant way to achieve the same. Is there any utility method already available in some standard third libraries like Guava, Apache Commons.
Splitting on "(?<=\\G.{7,}\\s)" produces the result that you need (demo).
\\G means the end of previous match; .{7,} means seven or more of any characters; \\s means a space character.
Not a standard method, but this might suit your needs
See it on http://ideone.com/2RFIZd
public static List<String> splitString(String str, int chunksize) {
char[] chars = str.toCharArray();
ArrayList<String> list = new ArrayList<String>();
StringBuilder builder = new StringBuilder();
int count = 0;
for(char character : chars) {
if(count < chunksize - 1) {
builder.append(character);
count++;
}
else {
if(character == ' ') {
builder.append(character);
list.add(builder.toString());
count = 0;
builder.setLength(0);
}
else {
builder.append(character);
count++;
}
}
}
list.add(builder.toString());
builder.setLength(0);
return list;
}
Please note, I used the human notation for string length, because that's what your sample reflects( 8 = postion 7 in string). that's why the chunksize - 1 is there.
This method takes 3 milliseconds on a text the size of http://catdir.loc.gov/catdir/enhancements/fy0711/2006051179-s.html
Splitting String using method 1.
String text="This is just a sample string";
List<String> strings = new ArrayList<String>();
int index = 0;
while (index < text.length()) {
strings.add(text.substring(index, Math.min(index + 8,text.length())));
index += 8;
}
for(String s : strings){
System.out.println("["+s+"]");
}
Splitting String using Method 2
String[] s=text.split("(?<=\\G.{"+8+"})");
for (int i = 0; i < s.length; i++) {
System.out.println("["+s[i]+"]");
}
This uses a hacked reduction to get it done without much code:
String str = "This is just a sample string";
List<String> parts = new ArrayList<>();
parts.add(Arrays.stream(str.split("(?<= )"))
.reduce((a, b) -> {
if (a.length() + b.length() <= 8)
return a + b;
parts.add(a);
return b;
}).get());
See demo using edge case input (that breaks some other answers!)
This splits after each space, then either joins up parts or adds to the list depending on the length of the pair.
So I was at a programming interview a few months ago and this problem tripped me up for some reason. There are a couple of solutions I can think of but most of them seem extremely inefficient. Though I've been programming in some capacity for years, I'm currently in college for a CS degree so my point of reference may be incomplete. I was hoping someone here might offer up some possible solutions:
"Given a set of strings and associated numerical 'values,' assemble a palindrome from these string whose value (defined by the sum of the strings used to create it) is the highest possible."
There were no limits to how many strings could be provided, some strings may not be used.
Example:
"asd" - 3
"dsa" - 5
"appa" - 1
Result would be "asdappadsa" with a value of 9.
My thought would be to try all strings in all orders, then drop off one, starting with the lowest valued one, but that solution is O(N!) and I'd assume that's not ok.
(Preferred languages are C and Java, but whatever works)
EDIT: Clarification. Each string provided can only be used once, and has to be used exactly as provided, though you may choose to not use any of the strings in your palindrome. You can not use substrings of provided strings, nor can you reverse the string.
Replace "all strings" with "all palindromes" and the problem space becomes much smaller.
Divide the strings into 26 subsets.
Strings beginning with x (for 'a' <= x <= 'z')
[or whatever the set of "letters" is]
Now divide them into another 26 subsets
Strings ending with y ( for 'a' <= y <= 'z')
Note each string appears in a "begins with" set and an "ends with" set.
Use these sets to guide creation of all possible palindromes.
Start with two empty strings: prefix and suffix
for each string in the original set
assign it to the prefix
call recursiveFunction(prefix, suffix)
def recursiveFunction(prefix, suffix):
if prefix + <anything> + suffix cannot form a palindrome return
if prefix + suffix is a palindrome, remember it
while you have unused strings
if the suffix is shorter than the prefix
Look at the first unmatched character in the prefix
for each unused string that ends in that character
call recursiveFunction(prefix, string + suffix)
else if prefix is shorter than suffix
look at the last unmatched character in the suffix
for each unused string that ends with that character
call recursiveFunction(prefix + string, suffix)
else // suffix and prefix have equal lenghths
for each unused string
call recursiveFunction(prefix + string, suffix)
Be sure to mark the string used in both begins with and ends when you use it.
And be sure to consider the impact of recursion on the "used" marker.
Then pick the palindrome with the best score.
With palindrones, you can split the string into 3 substrings, ABC. There is potential within the set to find string reversals, so when you get a hit you can keep track of these strings for useage in substring A and C. If you don't get a reversal hit within the entire set, you just need to find the largest string (or string with the highest value) that is a reflection of itself. Not the best solution, I'm sure, but I did it for fun anyways so I may as well share. Also, I made no efforts to make this code "clean", so its a bit ugly, but it works.
public class Main {
private static String[] set = new String[] { "asdf" , "kjdij", "skjdihi", "ddidid" , "ididid", "iddid", "oihu", "uhio", "fdsa", "ajsja" };
public static void main(String[] args){
//Used for a couple of for loops...
int count;
Map<String, String> map = new HashMap<String,String>(); //Hold the strings and their reversals in a hashmap for quick lookups.
/* Think of the palindrome as 3 substrings, A, B, and C. */
ArrayList<String> A = new ArrayList<String>();
ArrayList<String> B = new ArrayList<String>();
ArrayList<String> C = new ArrayList<String>();
count = set.length;
String[] reverse_set = new String[count];
for(int x = 0; x < count; x++){
String reverse = new StringBuilder(set[x]).reverse().toString();
/* Check strings that would work for section B, since it's a set, we know that if it could work for B,
* it couldn't work for A or C - since a set contains no duplicates. */
if(reverse.equals(set[x])){
B.add(set[x]);
} else {
/* Otherwise, we'll have to check to see if it would work for A and C */
reverse_set[x] = reverse;
map.put(set[x], reverse);
}
}
/* Check the map for reverse string hits, we know that if another string exists in set S such that S(X) == R(X) it can work for substring
* A and C. */
int map_size = map.size();
int hit_count = 0;
for(int i = 0; i < map_size; i++){
String s = map.get(reverse_set[i]);
if(s != null){
hit_count++;
A.add(set[i]);
C.add(reverse_set[i]);
map.remove(set[i]); //remove the reflection, so we don't get duplicates
}
}
/* Find the longest string in B, since we'll have to do this even if was had any hits in the previous loop. */
String b = null;
if(B.size() > 0){
int length = 0;
for(String x : B){
int mLength = x.length();
if(mLength > length){
b = x;
length = mLength;
}
}
}
/* Build the palinedrome */
String palineString = "";
if(hit_count == 0 && b != null){
palineString = b;
} else {
for(String x : A){
palineString += x;
}
if(b != null){
palineString += b;
}
count = C.size();
for(int y = (count - 1); y >= 0; y--){
palineString += C.get(y);
}
}
if(checkPalindrome(palineString)){
System.out.print("The Palindrone is: " + palineString + " Score: " + Integer.toString(palineString.length()));
} else {
System.out.print("Oops...");
}
}
private static boolean checkPalindrome(String x){
if(x.equals(new StringBuilder(x).reverse().toString())){
return true;
} else {
return false;
}
}
}
Output:
The Palindrone is: asdfoihuajsjauhiofdsa Score: 21
EDIT:
Note: for the time being, I used the string's length as the "assigned value", I'll make an edit later to account for assigning random values to strings, but it won't make too much of a difference, since it only adds a few extra use cases to check for.