Related
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 am trying to solve this problem in java. I have an arraylist of palindromic strings. I have to find the shortest palindrome string out of the given array list. I have solved the question but was looking at getting feedback on my code and also how I can try to make the code more efficient/better.
Here is the code what I have tried.
In this case, size would be 3, since that is the length of the smallest palindromic string.
import java.util.ArrayList;
class ShortestPalindrome {
public static int isShortestPalindrome(ArrayList<String> list) {
int smallest = list.get(0).length();
boolean ret = false;
for (String element : list) {
ret = isPalindrome(element);
if (ret) {
if (element.length() < smallest)
smallest = element.length();
}
}
return smallest;
}
private static boolean isPalindrome(String input) {
String str = "";
boolean result = false;
if (input.length() == 1 || input.length() == 0)
return true;
if (input.charAt(0) != input.charAt(input.length() - 1))
return false;
StringBuilder sb = new StringBuilder(input.toLowerCase());
str = sb.reverse().toString();
if (input.equals(str)) {
result = true;
}
return result;
}
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<String>();
array.add("malayam");
array.add("aba");
array.add("abcdeyugugi");
array.add("nitin");
int size = isShortestPalindrome(array);
System.out.println("Shortest length of string in list:" + size);
}
}
I have a couple of comments regarding your code:
In general - if you break your problem to smaller parts, there are efficient solutions all around.
As #AKSW mentioned in his comment, if - in any case - we have to check each string's length, it's better to do it in the beginning - so we don't run the relatively expensive method isPalindrome() with irrelevant strings.(Just notice I override the given list with the sorted one, even though initializing a new sorted list is trivial)
The main improvement that I made is in the isPalindrome() method:
Reversing a string of length n takes n time and additional n space. Comparing the two takes also n time.Overall: 2n time, n space
Comparing each two matching characters (from the beginning and from the end) takes 2 additional space (for the integers) and approximately n/2 time.Overall: n/2 time, 2 space
Obviously when using limits for complexity calculations, the time complexities are both the same - O(n) - but the second solution is still 4 times cheaper and cost a negligible amount of space.
Therefore I believe this is the most efficient way to achieve your test:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
class ShortestPalindrome {
public static int isShortestPalindrome(ArrayList<String> list) {
// Sorts the given ArrayList by length
Collections.sort(list, Comparator.comparingInt(String::length));
for (String element : list) {
if(isPalindrome(element)) {
return element.length();
}
}
return -1; // If there is no palindrome in the given array
}
private static boolean isPalindrome(String input) {
String lowerCased = input.toLowerCase();
int pre = 0;
int end = lowerCased.length() - 1;
while (end > pre) {
if (lowerCased.charAt(pre) != lowerCased.charAt(end))
return false;
pre ++;
end --;
}
return true;
}
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<>(Arrays.asList("malayam", "aba", "abcdeyugugi", "nitin"));
int size = isShortestPalindrome(array);
System.out.println("Shortest length of string in list: " + size);
}
}
Edit: I've tested this algorithm with the following list. Sorting the list before checking for palindromes reduces run time in 50%.
"malayam", "aba", "abcdeyugugi", "nitin", "sadjsaudifjksdfjds", "sadjsaudifjksdfjdssadjsaudifjksdfjds", "sadjsaudifjksdfjdssadjsaudifjksdfjdssadjsaudifjksdfjds", "a"
The simplest improvement to your code is to only check whether a string is a palindrome, if it's length is smaller than smallest.
Btw the initialization int smallest = list.get(0).length(); is not correct, imagine the first element not being a palindrome and being of smallest size of all strings. You should do int smallest = Integer.MAX_VALUE;
Also the check
if (input.charAt(0) != input.charAt(input.length() - 1))
return false;
is incorrect, as you don't convert the characters to lower case (as you do later), thus "ajA" would not be a palindrome.
There are further improvements of your code possible:
You could replace the palindrome checking by copying and reversing with this:
for (int i = 0; i < input.length() / 2; ++i)
if (Character.toLowerCase(input.charAt(i)) != Character.toLowerCase(input.charAt(input.length() - 1 - i)))
return false;
Here there is no copy necessary and in the average case it might be faster (as it can terminate early).
Also, like AKSW mentioned, it might be faster to sort the strings by length and then you can terminate early, once you found a palindrome.
Below simple code should work for you.
First check for length and then check is it actually palindrome or not.
If yes, then just store it in smallest
public static int isShortestPalindrome(ArrayList<String> list) {
Integer smallest = null;
for(String s:list){
if ( (smallest == null || s.length()< smallest) && new StringBuilder(s).reverse().toString().equalsIgnoreCase(s) ){
smallest = s.length();
}
}
return smallest == null ? 0 :smallest;
}
Here is a stream version:
OptionalInt minimalLenOfPalindrome
= list.paralellStream()
.filter(st -> {
StringBuilder sb = new StringBuilder(st);
String reversedSt = sb.reverse().toString();
return st.equalsIgnoreCase(reversedSt);
})
.mapToInt(String::length)
.min();
Thanks for #yassin answer I am changing above code with:
public class SOFlow {
private static boolean isPalindrome(String input) {
for (int i = 0; i < input.length() / 2; ++i) {
if (Character.toLowerCase(input.charAt(i)) != Character.toLowerCase(input.charAt(input.length() - 1 - i))) {
return false;
}
}
return true;
}
public static void main(String args[]) {
List<String> list = new ArrayList<>();
list.add("cAcc");
list.add("a;;;;a");
list.add("aJA");
list.add("vrrtrrr");
list.add("cAccccccccc");
OptionalInt minimalLenOfPalindrome
= list.parallelStream()
.filter(SOFlow::isPalindrome)
.mapToInt(String::length)
.min();
System.out.println(minimalLenOfPalindrome);
}
}
Using Java8 streams, and considering sorting first, for the same reasons as others:
boolean isPalindrome (String input) {
StringBuilder sb = new StringBuilder(input.toLowerCase());
return sb == sb.reverse();
}
public static int isShortestPalindrome(ArrayList<String> list) {
return (list.stream().sorted ((s1, s2) -> {
return s1.length () - s2.length ();
})
.filter (s-> isPalindrome (s))
.findFirst ()
.map (s -> s.length ())
.orElse (-1));
}
If you have many Strings of equal, minimal length, of very big length, only non palindromic in the very middle, you might spend much time in isPalindrome and prefer something like isPalindrome1 over isPalindrome.
If we assume a Million Strings of equally distributed length from 1000 to 2000 characters, we would end up with concentration on in avg. 1000 Strings. If most of them are equal except on few characters, close to the middle, then fine tuning that comparison might be relevant. But finding a palindrome early terminates our search, so the percentage of palindromes is of heavy influence on the performance too.
private static boolean isPalindrome1 (String s) {
String input = s.toLowerCase ();
int len = input.length ();
for (int i = 0, j = len -1; i < len/2 && j > len/2; ++i, --j)
if (input.charAt(i) != input.charAt (j))
return false;
return true;
}
The Result of the stream sorting and filtering is an Option, which is great opportunity to signal, that nothing was found. I sticked to your interface of returning int and return -1 if nothing is found, which has to be properly evaluated, of course, by the caller.
I was working on a question that requires a concatenation of strings recursively and ran into a problem.
Question states that s(0) = 0, s(1) = 1, s(n) = s(n-1)s(n-2) for n >= 2, where s(n) is the concatenated string of the previous two strings.
Input will indicate how many instances of (n, k) pair will be input as the first integer, followed by each line containing a non-negative integer
n (0 <= n <= 60) and a positive integer k.
Output is supposed to be printing out the kth character of the concatenated string s(n), where k is less or equal to the number of characters in string s(n).
s(0) = 0
s(1) = 1
s(2) = 10
s(3) = 101
s(4) = 10110
s(5) = 10110101
and so on.
Sample input:
3
5 2
0 1
4 3
Output:
0
0
1
My code:
import java.util.*;
public class recursivestring {
public static String recursive(int n, int i, String str1, String str2){
if (i == n - 1)
return str1 + str2;
return recursive(n, i + 1 , str1 + str2, str1);
}
public static void main(String[] args) {
int lines, i, n, k;
String result;
Scanner input = new Scanner(System.in);
lines = input.nextInt();
for (i = 0; i < lines; i++) {
n = input.nextInt();
k = input.nextInt();
if (n == 0) {
result = "0";
} else if (n == 1) {
result = "1";
} else if (n == 2) {
result = "10";
} else {
result = recursive(n, 2, "10", "1");
}
System.out.println(result.charAt(k-1));
}
}
}
This is what I have so far, and it works for the given sample test case. It works for most cases but once n becomes large, I get this error
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Why is that happening and is there something wrong with my code?
Thank you!
The problem with your approach is that it creates too many throw-away strings. Each time you write
return str1 + str2;
a new String object is created. The number of such throw-away objects grows linearly with n, while their total length grows as O(n2).
You have two solutions to this problem:
Keep your program linear, and pass StringBuilder at the top level. Each recursive invocation would call append, rather than using operator + for concatenation.
Use Memoization - since the number of strings that you need to compute is small, storing the ones that you computed so far and re-using them should fix the problem.
A bigger issue with your problem limits is that its output cannot fit in a String: Java allows strings of up to 231 in length, while the output of your code for 60 is quite a bit longer - namely, 1,548,008,755,920 characters. While you should be able to save this output in a file, there is no way to store it as a String, with or without memoization.
Since you're doing this in Java and not in an FP language with tail call optimization, two things are wrong: recursion and string concatenation. In Java you would to iteration and string building with a mutable builder.
Specifically, your recursive function retains all the interim string you produce on your way to the full string. That's O(n2) memory usage.
Well, to be honest this looks like fibonacci to me so me approach looks a little like that...
public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
System.out.println(stringbonacci(i));
}
}
private static String stringbonacci(int i) {
if (i == 0) {
return "0";
}
if (i == 1) {
return "1";
} else {
return stringbonacci(i - 1) + stringbonacci(i - 2);
}
}
and the result looks like:
0
1
10
101
10110
10110101
I highly recommend caching the result of each method call so that you don't have to re-calculate everything more than once. Will greatly improve your performance at the cost of a small bit of heap. Something like... this?
public class RecursiveString {
private static final Map<Integer, String> cache = new HashMap<>();
static {
cache.put(0, "0");
cache.put(1, "1");
}
public static String generateString(Integer i) {
if (i < 0) return generateString(0); // or something... avoid negatives.
if (cache.get(i) != null) return cache.get(i); // cache hit.
String generated = String.format("%s%s",
generateString(i-1), generateString(i-2));
cache.put(i, generated);
return generated;
}
}
Note that on a 2G heap, generateString works up through i=40 on my machine, simply due to the length of the resulting string. The string's length is going to be 2*fib(i) bytes in size, so your memory requirement is going to explode pretty quickly once you start hitting those indexes.
Same idea as the answer with the map (store the intermediate string, don't recompute), but using an array instead.
This could be optimized a little more by reading all the lines, then finding the maximum n value, thereby storing only one array, then looping back over the (n, k) pairs.
import java.util.Scanner;
public class BinaryConcat {
private String[] dict;
public BinaryConcat(int n) {
if (n < 0) throw new IllegalArgumentException();
dict = new String[2 + n]; // Have 2 base cases
dict[0] = "0";
dict[1] = "1";
}
public String getValue(int n) {
if (n < 0) throw new IllegalArgumentException();
if (n <= 1) return dict[n];
if (dict[n] == null || dict[n].isEmpty()) {
dict[n] = String.format("%s%s", getValue(n - 1), getValue(n - 2));
}
return dict[n];
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int lines = input.nextInt();
input.nextLine(); // consume linefeed
for (int i = 0; i < lines; i++) {
String line = input.nextLine();
String[] nk = line.split(" ");
int n = Integer.parseInt(nk[0]);
int k = Integer.parseInt(nk[1]);
String value = new BinaryConcat(n).getValue(n);
System.out.println(value.charAt(k - 1));
}
}
}
Sample run
(same input and output as expected)
The problem asks to "implement an algorithm to determine if a string has all unique character.
I saw the solution, but don't quite understand.
public boolean isUniqueChars(String str) {
if (str.length() > 256) return false;
boolean[] char_set = new boolean[256];
for (int i = 0; i < str.length(); i++) {
int val = str.charAt(i);
if (char_set[val])
return false;
char_set[val] = true;
}
return true;
}
Do we not use parseInt or (int) converter in front of the code? (Will str.charAt[i] be automatically changed to int?)
What does boolean[] char set=new boolean[256] mean?
Why do we need to set char_set[val]=true?
We can also use HashSet Data structure to determine if string has all unique characters in java.
Set testSet = new HashSet();
for (int i = 0; i < str.length(); i++) {
testSet.add(new Character(str.charAt(i)));
}
if (testSet.size() == str.length()) {
System.out.println("All charcaters are Unique");
} else {
System.out.println("All charcaters are niot unique");
}
See my explanation in the comments, since you only tagged algorithm I'm assuming no language and just address the algorithm itself:
public boolean isUniqueChars(String str){
//more than 256 chars means at least one is not unique
//please see first comment by #Domagoj as to why 256 length
if(str.length()>256) return false;
//keeping an array to see which chars have been used
boolean[] char_set = new boolean[256];
//iterating over the string
for(int i=0; i<str,length;i++){
//not sure what language this is, but let's say it returns an
//int representation of the char
int val=str.charAt(i);
//meaning this has been set to true before, so this char is not unique
if(char_set[val])
//nope, not unique
return false;
//remember this char for next time
char_set[val]=true;
}
//if we reached here, then string is unique
return true;
}
A simple solution would be to convert String to a Set and compare the lengths of corresponding objects. Use java 8:
private static boolean checkUniqueChars(String copy) {
if(copy.length() <= 1) return true;
Set<Character> set = copy.chars()
.mapToObj(e->(char)e).collect(Collectors.toSet());
if (set.size() < copy.length()){
return false;
}
return true;
}
One way you can do is via bits.
Time Complexity : O(N) (You could also argue the time complexity is 0(1), since the for loop will never iterate through more than
128 characters.)
Space Complexity : O(1)
Consider each char as bit(whether its there or not). Example, we need to check if all the chars are unique in "abcada", so if we will check if the bit for a char is already turned on, if yes then we return false otherwise set the bit there.
Now how we do it ? All the chars can be represented as numbers and then bits. We are gonna use "set bit" and "get bit" approach.
We will assume, in the below code, that the string only uses the lowercase letters a through z.
public static boolean isUniqueChars(String str) {
int mask = 0;
for (int i = 0; i < str.length(); ++i) {
int val = str.charAt(i) - 'a'; // you will get value as 0, 1, 2.. consider these as the positions inside your mask which you need to check if the bit is set or not
if ((mask & (1 << val)) > 0) return false; // Check if the bit is already set
mask |= (1 << val); // Set bit
}
return true;
}
To understand bit manipulation , you can refer
https://www.hackerearth.com/practice/notes/bit-manipulation/
https://snook.ca/archives/javascript/creative-use-bitwise-operators
It took me a while to understand bits but once I did, it was eye opening. You can solve so many problems using bits and provides most optimal solutions.
Think about how you would do this with a paper and pencil.
Write out the alphabet once.
Then go through your string character by character.
When you reach a character cross it out of your alphabet.
If you go to cross out a character and find that it has already been crossed out, then you know the character appeared previously in your string and you can then stop.
That's essentially what the code you posted does, using an array. The operation completes in O(N) time with O(K) extra space (where K is the number of keys you have).
If your input had a large number of elements or you could not know what they were ahead of time, you could use a hash table to keep track of which elements have already been seen. This again takes O(N) time with O(cK) extra space, where K is the number of keys and c is some value greater than 1.
But hash tables can take up quite a bit of space. There's another way to do this. Sort your array, which will take O(N log N) time but which requires no extra space. Then walk through the array checking to see if any two neighbouring characters are the same. If so, you have a duplicate.
You could see the detailed explanation in my blogpost here:
Check if string has all unique characters
The simplest solution is to make a loop through all characters, use hashMap and to put each character into the hashmap table, and before this check if the character is already there. If the character is already there, it's not unique.
public class UniqueString {
public static void main(String[] args) {
String input = "tes";
Map<String, Integer> map = new HashMap<String, Integer>();
for (int i = 0; i < input.length(); i++) {
if (!map.containsKey(Character.toString(input.charAt(i)))) {
map.put(Character.toString(input.charAt(i)), 1);
} else {
System.out.println("String has duplicate char");
break;
}
}
}
}
Java SE 9
You can simply match the length of the string with the count of distinct elements. In order to get the IntStream of all characters, you can use String#chars on which you can apply Stream#distinct to get the Stream of unique elements. Make sure to convert the string to a single case (upper/lower) otherwise the function, Stream#distinct will fail to count the same character in different cases (e.g. I and i) as one.
Demo:
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
// Test
Stream.of(
"Hello",
"Hi",
"Bye",
"India"
).forEach(s -> System.out.println(s + " => " + hasUniqueChars(s)));
}
static boolean hasUniqueChars(String str) {
return str.toLowerCase().chars().distinct().count() == str.length();
}
}
Output:
Hello => false
Hi => true
Bye => true
India => false
Java SE 8
static boolean hasUniqueChars(String str) {
return Arrays.stream(str.toLowerCase().split("")).distinct().count() == str.length();
}
For best performance, you should use a Set, and add the characters of the string to the set. If the set.add(...) method returns false, it means that the given character has been seen before, so you return false, otherwise you return true after adding all the characters.
For the simple solution, use Set<Character>:
public static boolean allUniqueCharacters(String input) {
Set<Character> unique = new HashSet<>();
for (int i = 0; i < input.length(); i++)
if (! unique.add(input.charAt(i)))
return false;
return true;
}
That will however not handle Unicode characters outside the BMP, like Emojis, so we might want to change the set to use Unicode Code Points:
public static boolean allUniqueCodePoints(String input) {
Set<Integer> unique = new HashSet<>();
return input.codePoints().noneMatch(cp -> ! unique.add(cp));
}
However, even Code Points do not represent "characters" as we humans think of them. For that we need to process Grapheme Clusters:
public static boolean allUniqueClusters(String input) {
BreakIterator breakIterator = BreakIterator.getCharacterInstance(Locale.US);
breakIterator.setText(input);
Set<String> unique = new HashSet<>();
for (int start = 0, end; (end = breakIterator.next()) != BreakIterator.DONE; start = end)
if (! unique.add(input.substring(start, end)))
return false;
return true;
}
Or with Java 9+:
public static boolean allUniqueClusters(String input) {
Set<String> unique = new HashSet<>();
return Pattern.compile("\\X").matcher(input).results()
.noneMatch(m -> ! unique.add(m.group()));
}
In JS,
const isStringUnique = str => {
if(str){
let obj = {}
for(let char of str){
obj[char] ? obj[char]++ : obj[char]=1;
}
for(let char of str){
if(obj[char] > 1)
return false;
}
return true;
}
return true;
}
public class CheckStringUniqueChars {
public static boolean checkUnique(String str) {
int i=0,j=str.length()-1;
while(i<j) {
if(str.charAt(i) == str.charAt(j)) {
return false;
}
i++;
j--;
}
return true;
}
}
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.