Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Algorithm to remove a character from a word such that the reduced word is still a word in dictionary

Here is the scenario, Given a word remove a single character from a word in every step such that the reduced word is still a word in dictionary. Continue till no characters are left.

Here is the catch: You need to remove the right character, for eg. in a word there may be two possible characters which could be removed and both may cause the reduced word to be a valid word, but at a later stage one may get reduced to the end i.e. no characters left while the other may hang up.

Example:

  • planet
  • plant
  • pant
  • pan
  • an
  • a

OR

  • planet
  • plane
  • lane
  • not possible further, suppose lan is not a word. hope you got the idea.

Please see my code, im using recursion, but would like to know if there are better efficient solutions to do the same.

public class isMashable
{

  static void initiate(String s)
  {
    mash("", s);
  }

  static void mash(String prefix, String s)
  {
    int N = s.length();
    String subs = "";

    if (!((s.trim()).equals("")))
      System.out.println(s);

    for (int i = 0 ; i < N ; i++)
    {
      subs = s.substring(0, i) + s.substring(i+1, N);
      if (subs.equals("abc")||subs.equals("bc")||subs.equals("c")||subs.equals("a")) // check in dictionary here
        mash("" + s.charAt(i), subs);
    }
  }

  public static void main(String[] args)
  {
    String s = "abc";
    initiate(s);
  }
}
like image 241
nmd Avatar asked Jun 28 '12 05:06

nmd


4 Answers

Here is an algorithm that uses depth first search. Given a word, you check if its valid (in dictionary). If its valid, remove one character from the string at each index and recursively check the 'chopped' word is valid again. If the chopped word is invalid at any point, you are in the wrong path and go back to previous step.

import java.util.HashSet;
import java.util.Set;

public class RemoveOneCharacter {
    static Set<String> dict = new HashSet<String>();

    public static boolean remove(String word){
        if(word.length() == 1)
            return true;

        if(!dict.contains(word))
            return false;

        for(int i=0;i<word.length();i++){
            String choppedWord = removeCharAt(word,i);
            boolean result = remove(choppedWord);
            if(result)
                return true;
        }
        return false;
    }

    public static String removeCharAt(String str, Integer n) {
        String f = str.substring(0, n);
        String b = str.substring(n+1, str.length());
        return f + b;
    }

    public static void main(String args[]){
        dict.add("heat");
        dict.add("eat");
        dict.add("at");
        dict.add("a");

        dict.add("planets");
        dict.add("planet");
        dict.add("plant");
        dict.add("plane");
        dict.add("lane");
        dict.add("plants");
        dict.add("pant");
        dict.add("pants");
        dict.add("ant");
        dict.add("ants");
        dict.add("an");


        dict.add("clean");
        dict.add("lean");
        dict.add("clan");
        dict.add("can");

        dict.add("why");

        String input = "heat";
        System.out.println("result(heat) " + remove(input));
        input = "planet";
        System.out.println("result(planet) " + remove(input));
        input = "planets";
        System.out.println("result(planets) " + remove(input));
        input = "clean";
        System.out.println("result(clean) " + remove(input));
        input = "why";
        System.out.println("result(why) " + remove(input));
        input = "name";
        System.out.println("result(name) " + remove(input));


    }

}
like image 170
plspl Avatar answered Sep 23 '22 19:09

plspl


Run a BFS algorithm. If you have more than one characters that you can remove, remove them individually and put in a priority queue, if you want to retrace the path, keep the pointer to the parent(the original word from which you created this word by removing a character) of the word in the node itslef. And when you remove all the characters, terminate and retrace the path, or if there is no valid way, you will have an empty priority queue

like image 2
Pankaj Jindal Avatar answered Oct 17 '22 03:10

Pankaj Jindal


I have used Porter Stemming in a couple of projects - that will of course only help you trim off the end of the word.

The Porter stemming algorithm (or ‘Porter stemmer’) is a process for removing the commoner morphological and inflexional endings from words in English. Its main use is as part of a term normalisation process that is usually done when setting up Information Retrieval systems.

A reprint occoured in M.F. Porter, 1980, An algorithm for suffix stripping, Program, 14(3) pp 130−137.

Martin even has a Java version available on his site.

like image 1
Niels Castle Avatar answered Oct 17 '22 01:10

Niels Castle


Here you go. The mash-method will find a solution (list of dictionary words) for any given String using a dictionary passed to the constructor. If there's no solution (ending to a one letter word), the method will return null. If you are interested in all partial solutions (ending before getting to a one letter word), you should tweak the algorithm a bit.

The dictionary is assumed to be a set of uppercase Strings. You could of course use your own class/interface instead.

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class WordMash {

    private final Set<String> dictionary;

    public WordMash(Set<String> dictionary) {
        if (dictionary == null) throw new IllegalArgumentException("dictionary == null");
        this.dictionary = dictionary;
    }

    public List<String> mash(String word) {
        return recursiveMash(new ArrayList<String>(), word.toUpperCase());
    }

    private List<String> recursiveMash(ArrayList<String> wordStack, String proposedWord) {
        if (!dictionary.contains(proposedWord)) {
            return null;
        }
        wordStack.add(proposedWord);

        if (proposedWord.length() == 1) {
            return wordStack;
        }

        for (int i = 0; i < proposedWord.length(); i++) {
            String nextProposedWord = 
                proposedWord.substring(0, i) + proposedWord.substring(i + 1, proposedWord.length());    
            List<String> finalStack = recursiveMash(wordStack, nextProposedWord);
            if (finalStack != null) return finalStack;
        }

        return null;
    }

}

Example:

Set<String> dictionary = new HashSet<String>(Arrays.asList(
        "A", "AFRICA", "AN", "LANE", "PAN", "PANT", "PLANET", "PLANT"
));
WordMash mash = new WordMash(dictionary);

System.out.println(mash.mash("planet"));
System.out.println(mash.mash("pant"));


System.out.println(mash.mash("foo"));
System.out.println(mash.mash("lane"));
System.out.println(mash.mash("africa"));
like image 1
COME FROM Avatar answered Oct 17 '22 02:10

COME FROM