Last week I came across this page where ITA Software posted a few puzzle challenges it used in its hiring process. I found the one named AddAGram very interesting and then decided to solve it.
Here is the question:
An "add-a-gram" is a sequence of words formed by starting with a 3-letter word, adding a letter and rearranging to form a 4-letter word, and so on. For example, here are add-a-grams of the words "CREDENTIALS" and "ANACHRONISM":
ail + s =
sail + n = nails + e = aliens + t = salient + r = entrails + c = clarinets + e = interlaces + d = CREDENTIALS (length 11) |
mar + c =
cram + h = march + s = charms + o = chromas + n = monarchs + i = harmonics + a = maraschino + n = ANACHRONISM (length 11) |
Test your own credentials: given the dictionary found here, what is the longest add-a-gram?
Initially I thought it would be solvable using a trie by inserting the words in dictionary in a trie and then simply searching the trie for the longest path down with each node after 3 node which is an end node ie. each node after third node marked as end of some word. But then I realized that it was wrong after half way coding it. Then I went back to square one and rethought the problem and then realized that it can be solvable only by searching. So then I came up with the following approach. I started with the longest word in the dictionary and then found the next word that is at a distance of 1 deletion from the previous word then pushed all those on a stack and for each word in the stack searched for next word at a distance of 1. So I basically created a graph from the words in dictionary t runtime and ran a DFS on it. The biggest hurdle or problem with this approach is searching through the whole dictionary for a word of that length and then backtracking when error occurs is very costly. So then I came up with a simple optimization technique to reduce the amount of words to be searched by storing the words in a array of vectors where vector at index i stores words of length i so dividing the access in 2 dimensions gives access to the all words of length i directly by calling dict[i].get(). Then finally we can rebuild the original sequence in the visited list of nodes.
It is written in java and it takes ~20 secs to run wit the dictionary of words given here.
Here is only the main logic part of the code:
You can find the complete program here
public class AddAGram{
public static Vector<String>dic[];
public static Stack<String>s;
public static HashSet<String>vis=new HashSet<String>();
public static boolean check(char []l,char []s){
int i,j,len=l.length;
for(i=0;i<s.length;i++)
for(j=0;j<l.length;j++)
if(s[i]==l[j]&&l[j]!=0) {s[i]=0;l[j]=0;len--;}
if(len==1) return true;
return false;
}
static void addAll(String str){
int l=str.length()-1,tot=dic[l].size();
String other="";
for(int i=0;i<tot;i++){
other=dic[l].elementAt(i);
if(check(str.toCharArray(),other.toCharArray())&&!vis.contains(other))
s.push(other);
}
}
static char diff(String small,String large){
StringBuffer sb=new StringBuffer(large);
for(int i=0;i<small.length();i++)
sb=sb.deleteCharAt(sb.indexOf(String.valueOf(small.charAt(i))));
return sb.charAt(0);
}
public static void generateOriginal(String start,String end){
LinkedList<String>list=new LinkedList<String>();
for(String a:vis) list.add(a);
String next="",prev=start;
list.remove(start);
int k,j=end.length();
for(int i=4;i<=j;){
for(k=0;k<list.size();k++){
if(list.get(k).length()==i){
if(check(list.get(k).toCharArray(),prev.toCharArray())){
next=list.remove(k);i++;
System.out.println(prev+"+"+diff(prev,next)+"="+next);
prev=next;break;
}
}
}
}
}
public static void main(String args[]) throws Exception{
dic=new Vector[30];
String str;int i,j,k;
for(i=0;i<30;i++) dic[i]=new Vector<String>();
InputReader sc=new InputReader(new FileInputStream("WORD.LST"));
long init=System.currentTimeMillis();
while(!sc.isExhausted()){
str=sc.readLine();dic[str.length()].add(str);
}
int z=0;
String word="",tmp="";Vector<String>v;
for(i=29;i>2;i--){
v=dic[i];
for(z=0;z<v.size();z++){
word=v.elementAt(z);
s=new Stack<String>();
vis.clear();
s.push(word);
while(!s.empty()){
tmp=s.pop();
addAll(tmp);
vis.add(tmp);
if(tmp.length()==3) {
generateOriginal(tmp,word);
System.out.println("Time taken : "+(System.currentTimeMillis()-init));
return;
}
}
}
}
}
}
0 comments:
Post a Comment