Encoding a String Array - java

EDIT : [Schoolwork Assignment]
So, I want to encode, with only 0 and 1, words.
0 = word not present
1 = word present
My dictionary correspond to :
String[] dictionary = {"Hello", "I", "am", "Lukas", "and", "Jonas", "play", "football"};
For example : If I encode these words...
String[] listOfWords = {"Hello", "play" "football"};
I must have the following array :
int[] wordsEncode = {1,0,0,0,0,0,1,1};
You can see that "Hello" is present, "I" "am" "Lukas" "and" "Jonas" are not present. Finally, "play" and "football" are present.
We must preserve the order of dictionary and that is my problem in my code.
I don't really know how to fix that problem (using a second for loop ?) ?
I think wordEncode[i] is my error, but how to fix that ?
Here is my code :
class Dictionary {
/**
* Array words Dictionary
*/
String[] dictionary;
/**
* Maximum of words MAX_WORDS
*/
final int MAX_WORDS = 50;
/**
* Number of words in the dictionary
*/
int numberWordsDictionary;
/**
* Constructor
*/
Dictionary() {
dictionary = new String[MAX_WORDS];
numberWordsDictionary = 0;
}
int[] encoder(String[] listOfWords) {
int[] wordsEncode = new int[numberWordsDictionary];
StringBuilder builder = new StringBuilder();
for(String word : dictionary) {
builder.append(word);
}
String dictionaryString = builder.toString();
for(int i = 0; i < listOfWords.length; i++) {
if(dictionaryString.contains(listOfWords[i])) {
wordsEncode[i] = 1;
} else {
wordsEncode[i] = 0;
}
}
return wordsEncode;
}
}
Sorry about indentation (not same as my Java IDE) :(
Thanks !

Using two-level nested loops, you should check each element of dictionary[] whether it is there in listOfWords[] and if yes, update the value at the corresponding index in wordsEncode[] to 1.
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
String[] dictionary = { "Hello", "I", "am", "Lukas", "and", "Jonas", "play", "football" };
String[] listOfWords = { "Hello", "play", "football" };
int[] wordsEncode = new int[dictionary.length];
for (int i = 0; i < dictionary.length; i++) {
boolean found = false;
for (String s : listOfWords) {
if (s.equals(dictionary[i])) {
found = true;
break;
}
}
if (found) {
wordsEncode[i] = 1;
}
}
// Display the result
System.out.println(Arrays.toString(wordsEncode));
}
}
Output:
[1, 0, 0, 0, 0, 0, 1, 1]

Loop your input words. For each input word, see if your list of target words contains that particular word. If so, add a 1 to your results list. If not, add zero.
I use the more convenient Collections, but you could do the same approach with arrays.
List< String > input = List.of( "Hello", "I", "am", "Lukas", "and", "Jonas", "play", "football" ) ;
List< String > targets = List.of( "Hello", "play" "football" ) ;
List< Integers > hits = new ArrayList<>() ;
for( String s : input )
{
int i = targets.contains( s ) ? 1 : 0 ;
hits.add( i ) ;
}

What you did here is iterate through dictionary array and added the words to a StringBuilder to check if a certain word you got in the listOfWords array is in the StringBuilder. But there a better solution to this you can just make a nested loop that compares every element of listOfWords array and dictionary array and if it found a match it sets the encode array value at the index of the second loop to 1:
int[] encoder(String[] listOfWords) {
int[] wordsEncode = new int[numberWordsDictionary];
for (int i = 0; i < listOfWords.length; i++) {
for (int j = 0; j < numberWordsDictionary; j++) {
if (listOfWords[i].equals(dictionary[j])) {
wordsEncode[j] = 1;
break;
}
}
}
return wordsEncode;
}

/* This approach is wrong, the combined string could catch words that are
part of the ending of one word and part of the beginning of another but
not actually a word in the dictionary. For instance, if you had
"arch" and "attach" in your dictionary, testing for "chat" would return true
*/
/*
StringBuilder builder = new StringBuilder();
for(String word : dictionary) {
builder.append(word);
}
String dictionaryString = builder.toString();
*/
for(int i = 0; i < listOfWords.length; i++) {
boolean found = false;
for (int j = 0; j < dictionary.length; j++) {
if (dictionary[j].equalslistOfWords[i]) {
found = true;
}
}
if (found) {
wordsEncode[i] = 1;
} else {
wordsEncode[i] = 0;
}
// you can also do wordsEncode[i] = found ? 1 : 0;
}
return wordsEncode;
}

Related

Generate permutations on list of strings using one character only

I am trying to generate permutations using list of strings taking one character one time.
Below is the code of input and output that I want.
Can we simply do it iteratively?. Also I am not finding exact method.
String[] lst = new String[]{"abc", "def", "ghi"}; //Given
String[] permutations = new String[]{ //To Generate
"adg", "adh", "adi",
"aeg", "aeh", "aei",
"afg", "afh", "afi",
"bdg", "bdh", "bdi",
"beg", "beh", "bei",
"bfg", "bfh", "bfi",
"cdg", "cdh", "cdi",
"ceg", "ceh", "cei",
"cfg", "cfh", "cfi",
};
Update: I am not looking just for the above example with list size=3. It can be of any size and each string may happen to be of different length.
For ex: list = [ "ab", "abc", "defghi", "x", "einsigl"]
In this answer I will walk through how I solved this problem to find an algorithm that works for an array of any length for words which can be any length and are not required to all be the same length.
I will first make a recursive solution, and then transorm it into an iterative one.
The easiest way to answer problems like this is to think of them recursively:
Generating all permutations of [] should return [""]
Generating all permutations of a non-empty list means, for each letter c in the first word in the list, return all permutations of the rest of the list with c prepended on the front.
This can be written in Java as follows:
public static List<String> generatePermutationsRecursiveSlow(String[] words) {
if (words.length == 0)
// base case
return Collections.singletonList("");
else {
// recursive case
// result list
ArrayList<String> permutations = new ArrayList<>();
// split array into item 0 and items [1..end]
String firstWord = words[0];
String[] otherWords = new String[words.length - 1];
System.arraycopy(words, 1, otherWords, 0, words.length - 1);
// recurse to find permutations for items [1..end]
List<String> otherWordsPermutations = generatePermutationsRecursiveSlow(otherWords);
// for each character in the first word
for (char c : firstWord.toCharArray()) {
// for each permutation from the recursive call's results
for (String otherWordsPermutation : otherWordsPermutations) {
// prepend this character onto the permutation and add it to the results
permutations.add(c + otherWordsPermutation);
}
}
return permutations;
}
}
Calling generatePermutationsRecursiveSlow(new String[0]) returns [""].
Calling generatePermutationsRecursiveSlow(new String[]{"cd"}) will cause the local c variable to be equal to 'c', and it will recurse with an empty array as the argument, making otherWordsPermutations equal to [""], so it will add 'c' + "" (which is "c") to the results, then it will do the same for 'd', adding "d" to the results.
Calling generatePermutationsRecursiveSlow(new String[]{"ab", "cd"}) will mean that when c is 'a', it will add to the results list 'a'+"c", then 'a'+"d", and whencis'b', it will add'b'+"c"and'b'+"d"`
A similar but better optimised version which works in the same way can be written like this:
public static List<String> generatePermutationsRecursive(String[] words) {
ArrayList<String> permutations = new ArrayList<>();
int wordLen = words.length;
generatePermutationsRecursive(words, permutations, new char[wordLen], 0);
return permutations;
}
public static void generatePermutationsRecursive(String[] words, ArrayList<String> permutations, char[] word, int i) {
if (i == word.length) {
// base case
permutations.add(new String(word));
} else {
for (int j = 0; j < words[i].length(); j++) {
// equivalent of prepending
word[i] = words[i].charAt(j);
// recurse
generatePermutationsRecursive(words, permutations, word, i + 1);
}
}
}
This is better optimised since it uses the word parameter to avoid the O(n) prepending to the string by instead modifying a character array. It also introduces the parameter i which is the effective start index of the array, making it possible to avoid copying parts of the input array.
This can be transformed into an iterative approach by tracking the variables that change between different recursive calls using a stack (in place of the call stack):
private static List<String> generatePermutationsIterative(String[] words) {
// in the recursive version, each recursive function call would have its own local copy of `i` and `j`
// simulate that here with 2 stacks
ArrayDeque<Integer> i_stack = new ArrayDeque<>(words.length);
ArrayDeque<Integer> j_stack = new ArrayDeque<>(words.length);
i_stack.add(0);
j_stack.add(0);
char[] word = new char[words.length];
ArrayList<String> permutations = new ArrayList<>();
while (!i_stack.isEmpty()) {
int i = i_stack.removeLast();
int j = j_stack.removeLast();
if (i == words.length) {
// base case
permutations.add(new String(word));
continue;
}
if (!(j < words[i].length())) {
// reached end of loop `for (int j = 0; j < words[i].length(); j++)`
continue;
}
// if not reached end of loop `for (int j = 0; j < words[i].length(); j++)` yet,
// then increment `j` and allow next iteration to happen
i_stack.add(i);
j_stack.add(j + 1);
word[i] = words[i].charAt(j);
// recurse
i_stack.add(i + 1);
j_stack.add(0);
}
return permutations;
}
Code here
As a sidenote, look how cool Haskell is with this 2-line solution to the problem here (admittedly its not iterative, but it should have tail-call optimisation, making it as fast as an iterative solution).
Here's one way to do it that should work for arbitrary number of words of arbitrary length (not including 0).
String[] lst = new String[] {
"abc",
"def",
"ghi"
};
int numWords = lst.length;
int wordlen = lst[0].length();
int numPerms = (int) Math.pow(wordlen, numWords);
char[][] perms = new char[numPerms][numWords];
char[][] chararr = Arrays.stream(lst)
.map(String::toCharArray)
.toArray(i -> new char[i][wordlen]);
for (int i = 0; i < numWords; i++) {
double permsLocal = Math.pow(wordlen, i + 1);
int numRepeats = (int) Math.ceil((numPerms / permsLocal));
int repeats = (int)(permsLocal / wordlen);
for (int x = 0; x < repeats; x++) {
char[] word = chararr[i];
for (int j = 0; j < wordlen; j++) {
char c = word[j];
for (int k = 0; k < numRepeats; k++) {
perms[(x * wordlen * numRepeats) + k + j * numRepeats][i] = c;
}
}
}
}
String[] permutations = Arrays.stream(perms)
.map(String::new)
.toArray(String[]::new);
Output:
[adg, adh, adi, aeg, aeh, aei, afg, afh, afi, bdg, bdh, bdi, beg, beh,
bei, bfg, bfh, bfi, cdg, cdh, cdi, ceg, ceh, cei, cfg, cfh, cfi]
Link to repl.it: https://repl.it/repls/BoilingExcitingAttributes
You can do it as follows:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
String[] lst = new String[] { "abc", "def", "ghi" };
List<String> list = new ArrayList<>();
for (char a : lst[0].toCharArray()) {
for (char b : lst[1].toCharArray()) {
for (char c : lst[2].toCharArray()) {
list.add(new String(new char[] { a, b, c }));
}
}
}
// Convert to array
String[] permutations = list.toArray(new String[0]);
// Display
System.out.println(Arrays.toString(permutations));
}
}
Output:
[adg, adh, adi, aeg, aeh, aei, afg, afh, afi, bdg, bdh, bdi, beg, beh, bei, bfg, bfh, bfi, cdg, cdh, cdi, ceg, ceh, cei, cfg, cfh, cfi]

How to check if an array contains a specific number of values?

import java.util.Scanner;
public class CountVowel{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
Getting the size of the array:
System.out.println("Type how many words will be typed: ");
int input = scan.nextInt();
Filling array with string values
String[] ar1 = new String[input];
for(int i = 0; i < ar1.length; i++){
System.out.println("Type the elements of array with words: ");
ar1[i] = scan.next();
}
Output of the program :
System.out.println( input + " words are typed and " +
countVowels(ar1) +
" of them contain more than 3 vowels.");
}
The method that counts vowels:
public static int countVowels(String[] ar1){ // this method counts
int a = 0;
String[] ar2 = new String[]{"a", "e", "i", "u", "y", "o"};
for(int i = 0; i < ar1.length; i++){
for(String s : ar2){
if(ar1[i].toLowerCase().contains(s)){
a++;
}
}
}
return a;
}
}
The method above is to check the vowels, but i dont know how to make it check if
there are more than 3 vowels.
Another solution with replaceAll method.
The main idea is to substract from word.length() the same word length without vowels. And check the difference.
public static int countVowels(String[] ar1){
int a = 0;
for (String word : ar1) {
int i = word.length() - word.toLowerCase().replaceAll("[aeyiuo]", "").length();
if (i >= 3) {
a++;
}
}
return a;
}
Or you can use matches() as #pkgajulapalli suggested. It can be quite concise with stream api:
long count = Arrays.stream(words)
.filter(s -> s.toLowerCase().matches("(.*[aeyiuo].*){3,}"))
.count();
public static int countVowels(String[] ar1) { // this method counts
int vowelPerWord = 0;
int totalWordsWithThreeVowels = 0;
char[] ar2 = new char[] { 'a', 'e', 'i', 'u', 'y', 'o' };
for (int i = 0; i < ar1.length; i++) {
vowelPerWord = 0;
for (int j = 0; j < ar1[i].length(); j++) {
for (int k = 0; k < ar2.length; k++) {
if (ar2[k] == (ar1[i].charAt(j))) {
vowelPerWord++;
}
}
}
if (vowelPerWord >= 3) {
totalWordsWithThreeVowels++;
}
}
return totalWordsWithThreeVowels;
}
EDIT
alright now i fixed the error and edited the variablenames to make a bit more sense. although this is O(n*m) i believe (where n is the number of strings and m is the number of char the longest string has) (not so good complexity) it gets the job done ar1 in this case is your input of strings, ar2 are just the vowels that exist.
so you go through every string in ar1 and set "vowelPerWord" to 0, go through every single char in every string and check if it is a vowel increase the vowelPerWord by 1. at the end, after you went through every char of that string you check if there were 3 or more vowels, if so increase the totalWordsWithThreeVowels, which at the end is returned.
What you need is an additional loop and count. Something like this:
// This method counts how many words have at least 3 vowels
public static int countVowels(String[] wordsArray){
int atLeastThreeVowelsCount = 0;
for(String word : wordsArray){
int vowelCount = 0;
for(String vowel : new String[]{ "a", "e", "i", "u", "y", "o" }){
if(word.toLowerCase().contains(vowel)){
vowelCount++;
}
}
if(vowelCount >= 3){
atLeastThreeVowelsCount++;
}
}
return atLeastThreeVowelsCount;
}
Try it online.
Note that I've also given the variables some more useful names, instead of ar1, s, etc. so it's easier to read what's going on.
You can use regex matching to find if a string contains any set of characters. For example, if you want to find if a string contains any of vowels, you can use:
String str = "yydyrf";
boolean contains = str.toLowerCase().matches(".*[aeiou].*");
System.out.println(contains);
EDIT:
So your code would look like:
public static int countVowels(String[] ar1) {
int a = 0;
String[] ar2 = new String[] { "a", "e", "i", "u", "y", "o" };
String pattern = ".*[" + String.join("", ar2) + "].*";
for (int i = 0; i < ar1.length; i++) {
if (ar1[i].matches(pattern)) {
a++;
}
}
return a;
}
You can use this:
public static int countVowels(String[] words) {
char[] chars = {'a', 'e', 'i', 'u', 'y', 'o'};
int wordsWith3Vowels = 0;
for (String word : words) {
int countedVowels = 0;
for (char s : chars) {
if (word.toLowerCase().indexOf(s) != -1) {
countedVowels++;
}
}
if (countedVowels >= 3) {
wordsWith3Vowels++;
}
}
return wordsWith3Vowels;
}
Which uses chars instead of Strings which is a tad faster
public static int countVowels(String[] ar1){ // this method counts
//Create hash map key = array string && value = vowels count
Map<String,Integer> mapVowels=new HashMap<String,Integer>();
int a = 0;
String[] ar2 = new String[]{"a", "e", "i", "u", "y", "o"};
for(int i = 0; i < ar1.length; i++){
for(String s : ar2){
if(ar1[i].toLowerCase().contains(s)){
//Check map string already has vowel count then increase by one
if(mapVowels.get(s)!=null) {
mapVowels.put(s,mapVowels.get(s)+1);
//After add the vowels count get actual count and check is it more than 3
if(mapVowels.get(s)>3)
a++;
}
else {
//If the vowels string new for map then add vowel count as 1 for first time
mapVowels.put(s,1);
}
}
}
}
return a;
}
Since java-8 you can now use Streams.
String[] values = {"AA","BC","CD","AE"};
boolean contains = Arrays.stream(values).anyMatch("s"::equals);
To check whether an array of int, double or long contains a value use IntStream, DoubleStream or LongStream respectively.
Example
int[] a = {1,2,3,4};
boolean contains = IntStream.of(a).anyMatch(x -> x == 4);

Longest Sequence in array with matching last and first character

I recently was asked a question to create a sequence from an array of string such that the string elements can combine if the last character of element 1 matches the first character of element 2.
Eg: {"ab", "bc", "cd", "ad", "def", "cedd"} should return "abceddef".
What I'm getting for above input is "abcdef".
public class LongestSubstringConsecutiveEnds {
static StringBuilder sbMax = new StringBuilder();
static StringBuilder sbTemp;
public static void main(String[] args) {
String[] inputStrings = {"ab", "bc", "cd", "ad", "def", "cedd"};
List<String> inputList = new ArrayList<String>(Arrays.asList(inputStrings));
for(int i=0; i<inputList.size(); i++) {
String str = inputList.get(i);
sbTemp = new StringBuilder(str);
inputList.remove(str);
longestSequence(sbTemp, new ArrayList<String>(inputList));
inputList.add(0, str);
}
System.out.println(sbMax.toString());
}
private static void longestSequence(StringBuilder tempSubstring, final List<String> inputList) {
System.out.println(tempSubstring.toString() + inputList);
if(tempSubstring.length() > sbMax.length()) {
sbMax.delete(0, sbMax.length());
sbMax.append(tempSubstring);
}
for(int i=0; i<inputList.size(); i++) {
String inputListString = inputList.get(i);
char tempStrLastChar = tempSubstring.charAt(tempSubstring.length()-1);
if(inputListString.charAt(0) == tempStrLastChar) {
String str = inputList.remove(i);
longestSequence(tempSubstring.append(inputListString.substring(1)), inputList);
inputList.add(i, str);
}
}
}
}
According your question:
if the last character of element 1 matches the first character of element 2.
Part you described in your question can be done easy:
for (int i = 0; i < strings.length - 1; i++) {
// last char of element i is equal first char of element i+1
if (strings[i].charAt(strings[i].length()-1) == strings[i+1].charAt(0)) {
// print element i.
System.out.print(strings[i]);
}
}
Output:
cd
That is, position 3 matches with 4 (cd-def)
But that does not match with
should return "abceddef"
And I can't find a logic... where last ef comes from? you mean match when for example last is a and first is b ??. That would be:
for (int i = 0; i < strings.length - 1; i++) {
// get last and first char
String actual = strings[i];
char last = actual.charAt(actual.length()-1);
char first = strings[i+1].charAt(0);
if ((int) first == last + 1) {
System.out.print(actual);
}
}
Output:
ab
That is, position 2 matches with 3 (ab-cd)
public static void main(String...args){
final String[] input={"ab", "bc", "cd", "ad", "def", "cedd"};
System.out.println( funkyConcat(input));
}
private static String funkyConcat(String...strings){
String ret="";
for(String i:strings){
if(ret.isEmpty()){
ret=i;
}else if(ret.substring(ret.length()-1).equals(i.substring(0,1))){
ret+=i.substring(1);
}
}
return ret;
}

Copy String array and remove empty strings

I want to eliminate empty elements within my String array. This is what I have tried so far:
String version = null;
String[] xml = new String[str.length];
for(int i = 0; i <= str.length -1; i++)
{
if(str[i] == "")
{
}
else
{
xml[i] = str[i];
}
}
String version = null;
String[] xml = new String[str.length];
for(int i = 0; i <= str.length -1; i++)
{
if(!str[i].equals(""))
{
xml[i] = str[i];
}
}
String version = null;
String[] xml = new String[str.length];
for(int i = 0; i <= str.length -1; i++)
{
if(!str[i].isEmpty())
{
xml[i] = str[i];
}
}
String version = null;
String[] xml = new String[str.length];
for(int i = 0; i <= str.length -1; i++)
{
if(str[i].isEmpty() == false)
{
xml[i] = str[i];
}
}
No matter which one I try, it always copies all the values. I've checked the locals, and it is clear that there are empty arrays within the String array.
Try this,
b = Arrays.copyOf(a, a.length);
Or
b = new int[a.length];
System.arraycopy(a, 0, b, 0, b.length);
Or
b = a.clone();
You are copying the same length array and using the same indexes. The length is always going to be the same.
List<String> nonBlank = new ArrayList<String>();
for(String s: str) {
if (!s.trim().isEmpty()) {
nonBlank.add(s);
}
}
// nonBlank will have all the elements which contain some characters.
String[] strArr = (String[]) nonBlank.toArray( new String[nonBlank.size()] );
String str[] = {"Hello","Hi","","","Happy","","Hmm"};
int count = 0;// Thisreprents the number of empty strings in the array str
String[] xml = new String[str.length];
for(int i = 0,j=0; i <= str.length -1; i++)
{
if(str[i].equals(""))
{
count++;
}
else
{
xml[j] = str[i];j++;
}
}
String a[] = Arrays.copyOfRange(xml, 0, xml.length-count);//s is the target array made by copieng the non-null values of xml
for(String s:a){
System.out.println(s);
}
NOTE : This may not be an efficient solution but it will give the result as per your requirement
This is just an alternate solution since you didn't want ListArray.
Read the comments in the code to clearly understand the logic.
int i,j=0,cnt=0;
//The below for loop is used to calculate the length of the xml array which
//shouldn't have any empty strings.
for(i=0;i<str.length;i++)
if(!isEmpty(str[i])
cnt++;
//Creation of the xml array with proper size(cnt) and initialising i=0 for further use
String xml[]=new String[cnt];
i=0;
//Simply copying into the xml array if str[i] is not empty.Notice xml[j] not xml[i]
while(i<str.length)
{
if(!isEmpty(str[i]))
{
xml[j]=str[i];
i++;
j++;
}
else
i++;
}
That should do the work.
Also I would suggest to not work with the 0th position of array as it kinda creates confusion for .length functions.Thats only my view. If you are comfortable with it,carry on! :D
If you are looking for a high-performance solution I think that this is the best solution. Otherwise if your input array is not so huge, I would use a solution similar to Peter Lawrey one, so it makes your code easy to understand.
With this solution you loop the input array only one, and if you don't need the input array any more you can avoid one array copy calling filterBlankLines with preserveInput = false.
public class CopyingStringArrayIntoNewStringArray {
public static void main(String[] args) {
String[] str = { "", "1", "", null, "2", " ", "3", "" };
System.out.println("\nBefore:");
printArrays(str);
String[] xml = filterBlankLines(str, true);
System.out.println("\nAfter:");
printArrays(xml);
}
private static String[] filterBlankLines(String input[], boolean preserveInput ) {
String[] str;
if (preserveInput) {
str = new String[input.length];
System.arraycopy(input, 0, str, 0, input.length);
} else {
str = input;
}
// Filter values null, empty or with blank spaces
int p=0, i=0;
for (; i < str.length; i++, p++) {
str[p] = str[i];
if (str[i] == null || str[i].isEmpty() || (str[i].startsWith(" ") && str[i].trim().isEmpty())) p--;
}
// Resize the array
String[] tmp = new String[ p ];
System.arraycopy(str, 0, tmp, 0, p);
str = null;
return tmp;
}
private static void printArrays(String str[]) {
System.out.println( "length " + str.length);
for (String s : str ) {
System.out.println(">"+s+"<");
}
}
}
The output:
Before:
length 8
><
>1<
><
>null<
>2<
> <
>3<
><
After:
length 3
>1<
>2<
>3<
This is the best and short way to copy an array into the new array.
System.arraycopy(srcArray, 0, destArray, 1, destArray.length -1);

Finding a word on a two dimensional char array

What kind of approch could be an easy way to find the given words on a puzzle like this? I'm using Java. Thanks for help.
Interesting question. I would solve this by first building a list of "possible word holders" (sequences of characters which can possibly hold one of the given words) by traversing the puzzle horizontally, vertically and diagonally (in both directions). I would then see if the given words (or their reverse) are present (using contains() method in Java) in each of the obtained "possible word holders". Here is the code I wrote in Java. I haven't tested it properly, but I guess it works!
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class WordPuzzle {
public Set<String> findWords(char[][] puzzle, Set<String> words) {
Set<String> foundWords = new HashSet<String>();
int minimumWordLength = findMinimumWordLength(words);
Set<String> possibleWords = findPossibleWords(puzzle, minimumWordLength);
for(String word : words) {
for(String possibleWord : possibleWords) {
if(possibleWord.contains(word) || possibleWord.contains(new StringBuffer(word).reverse())) {
foundWords.add(word);
break;
}
}
}
return foundWords;
}
private int findMinimumWordLength(Set<String> words) {
int minimumLength = Integer.MAX_VALUE;
for(String word : words) {
if(word.length() < minimumLength)
minimumLength = word.length();
}
return minimumLength;
}
private Set<String> findPossibleWords(char[][] puzzle, int minimumWordLength) {
Set<String> possibleWords = new LinkedHashSet<String>();
int dimension = puzzle.length; //Assuming puzzle is square
if(dimension >= minimumWordLength) {
/* Every row in the puzzle is added as a possible word holder */
for(int i = 0; i < dimension; i++) {
if(puzzle[i].length >= minimumWordLength) {
possibleWords.add(new String(puzzle[i]));
}
}
/* Every column in the puzzle is added as a possible word holder */
for(int i = 0; i < dimension; i++) {
StringBuffer temp = new StringBuffer();
for(int j = 0; j < dimension; j++) {
temp = temp.append(puzzle[j][i]);
}
possibleWords.add(new String(temp));
}
/* Adding principle diagonal word holders */
StringBuffer temp1 = new StringBuffer();
StringBuffer temp2 = new StringBuffer();
for(int i = 0; i < dimension; i++) {
temp1 = temp1.append(puzzle[i][i]);
temp2 = temp2.append(puzzle[i][dimension - i - 1]);
}
possibleWords.add(new String(temp1));
possibleWords.add(new String(temp2));
/* Adding non-principle diagonal word holders */
for(int i = 1; i < dimension - minimumWordLength; i++) {
temp1 = new StringBuffer();
temp2 = new StringBuffer();
StringBuffer temp3 = new StringBuffer();
StringBuffer temp4 = new StringBuffer();
for(int j = i, k = 0; j < dimension && k < dimension; j++, k++) {
temp1 = temp1.append(puzzle[j][k]);
temp2 = temp2.append(puzzle[k][j]);
temp3 = temp3.append(puzzle[dimension - j - 1][k]);
temp4 = temp4.append(puzzle[dimension - k - 1][j]);
}
possibleWords.add(new String(temp1));
possibleWords.add(new String(temp2));
possibleWords.add(new String(temp3));
possibleWords.add(new String(temp4));
}
}
return possibleWords;
}
public static void main(String args[]) {
WordPuzzle program = new WordPuzzle();
char[][] puzzle = {
{'F','Y','Y','H','N','R','D'},
{'R','L','J','C','I','N','U'},
{'A','A','W','A','A','H','R'},
{'N','T','K','L','P','N','E'},
{'C','I','L','F','S','A','P'},
{'E','O','G','O','T','P','N'},
{'H','P','O','L','A','N','D'}
};
Set<String> words = new HashSet<String>();
words.add("FRANCE");
words.add("POLAND");
words.add("INDIA");
words.add("JAPAN");
words.add("USA");
words.add("HOLLAND");
Set<String> wordsFound = program.findWords(puzzle, words);
for(String word : wordsFound) {
System.out.println(word);
}
}
}
In general, I say use the most naive approach unless your puzzles are going to be large. I wouldn't optimize anything that takes less than 0.1s, but thats just me.
foreach box
for all directions
grab the string of characters in that direction
lookup a dictionary
I think the smarts can be in how you design your dictionary. In this case, I would do a multi-level hash table where characters pick which hash table to look at the next level.
I would put the word list into a Trie, then do a search from all squares in all directions.
The easiest approach (conceptualy) is to simply enumerate all possible words in your array and check all of then in a dictionnary. A dictionnary behing a map, an array of string... or a real dictionnary downloaded from the internet.
As an exemple here is the code to find all possible word horizontally... Adding other direction is just more work :
import java.util.HashSet;
import java.util.Set;
public class WordFinder {
public static void main(String[] args) {
String[][] words = { { "F", "Y", "Y", "H", "N", "R", "D" },
{ "R", "L", "J", "C", "I", "N", "U" },
...};
Set<String> dictionnary = new HashSet<String>();
dictionnary.add(...);
Set<String> wordsFound = findWords(words, dictionnary);
...
}
/**
* Find all words in the specified array present in the dictionnary.
*
*/
private static Set<String> findWords(String[][] words, Set<String> dictionnary) {
Set<String> wordsFound = new HashSet<String>();
// Find all possible words horizontally :
int nbrRows = words.length;
int nbrCol = words[0].length; // We suppose we have at least one row and all row have same lengh
// Iterate through all rows
for (int currentRow = 0; currentRow < nbrRows; currentRow++) {
// Iterate through all possible starting position in the current row.
for (int beginWordIndex = 0; beginWordIndex < nbrCol; beginWordIndex++) {
// Iterate then through all possible ending positions in the current row, so to deal with word of any lengh.
for (int endWordIndex = beginWordIndex; endWordIndex < nbrCol; endWordIndex++) {
// Construct a word from the begin/end indexes :
String currentWord = getWordInRow(words, currentRow, beginWordIndex, endWordIndex);
// Check if the word candidate really exist, if yes, store it in the wordsFound variable.
if (dictionnary.contains(currentWord)) {
wordsFound.add(currentWord);
}
// The reverse
String reverseWord = reverseString(currentWord);
// Check if the reverse word really exist, if yes, store it in the wordsFound variable.
if (dictionnary.contains(reverseWord)) {
wordsFound.add(currentWord);
}
}
}
}
// Don't forget vertically and in diagonals too... Same principe.
return wordsFound;
}
/**
* Return a word "candidate" in the specified row, starting at beginIndex and finishing at endIndex.
*/
private static String getWordInRow(String[][] words, int row, int beginIndex, int endIndex) {
String currentWord = "";
int currentPosition = beginIndex;
while (currentPosition <= endIndex) {
currentWord += words[row][currentPosition];
}
return currentWord;
}
/**
* Return the reverse of a String
*/
private static String reverseString(String string) {
String result = "";
for (int i = string.length()-1; i >=0;i++) {
result+= string.charAt(i);
}
return result;
}
}
This is not the best, most effective solution. But it is conceptually simple.
EDIT :
reverse order: see edited code. Just write a function that can reverse a word. Because we already have all posible word in normal order, reversing them is enough to have words in reverse order.
Diagonals : I'am sure you can do it if you have understood the code I have already put. I will not do your homework or do your testing in place of you. Try to figure how you would do it with a paper and a pen. How would you do it if you had to do it by hand. Then from that, write your solution ;)

Categories