Pictured here is a keypad:
We want to find
all sequences of length n that can be
keyed into the keypad in the following manner:
- The initial keypress can be any of the
keys.
- Each subsequent keypress must be a knight move from the previous
keypress.
- There can be at most 2 vowels in the
sequence.
- Run your solution for n = 1 to 32
A knight move is
made in one of the following ways:
- Move two steps horizontally and one
step vertically.
- Move two steps vertically and one step horizontally.
- There is no wrapping allowed on a knight move.
Here are some
examples of knight moves:
Given a value for n, the program should write the number
of valid n-key sequences on a single
line to standard out.
Knight Moves Solution
package com.denizstij.km;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.denizstij.km.Keyboard.Key;
/**
*
* @author Deniz Turan, http://denizstij.blogspot.co.uk denizstij AT gmail.com
*
*/
public class KnightMoveMain {
private final int maxVowelCount;
public KnightMoveMain(int maxVowelCount){
this.maxVowelCount=maxVowelCount;
}
public long findNumberOfSequence(int lenOfSequence) {
if (lenOfSequence<=0){
return 1;// number of empty set is 1
}
Map <PathInfoBean, Long> pathInfoMap= new LinkedHashMap<PathInfoBean, Long>();
for (Key key : Keyboard.INSTANCE.getKeySet()) {
PathInfoBean pathinfo= new PathInfoBean(key, key.isVowel()?1:0);
pathInfoMap.put(pathinfo, 1L);
}
long numberOfPaths = findNumberOfSequence1(pathInfoMap,1,lenOfSequence);
return numberOfPaths;
}
private long findNumberOfSequence1(Map <PathInfoBean, Long> pathInfoMap ,int sequenceLen, int lenOfSequence) {
if (sequenceLen>=lenOfSequence){
long numberOfPaths=0;
for (Long pathCount : pathInfoMap.values()) {
numberOfPaths+=pathCount;
}
return numberOfPaths;
}
Map <PathInfoBean, Long> pathInfoMapNew= new LinkedHashMap<PathInfoBean, Long>();
for (Entry<PathInfoBean, Long> pathInfoBeanEntry : pathInfoMap.entrySet()) {
PathInfoBean pathInfoBean = pathInfoBeanEntry.getKey();
Long pathCount = pathInfoBeanEntry.getValue();
Set<key> possibleMoveSet =pathInfoBeanEntry.getKey().getKey().getKnightMoves();
for (Key key : possibleMoveSet) {
int vowelCount=pathInfoBean.getVowelCount();
if (key.isVowel()){
if (vowelCount>=maxVowelCount){
continue;
}
vowelCount++;
}
PathInfoBean newPathInfoBean = new PathInfoBean(key,vowelCount);
Long currentPath = pathInfoMapNew.get(newPathInfoBean);
if (currentPath==null) // first time
pathInfoMapNew.put(newPathInfoBean,pathCount);
else {
// we have already, so just lets aggregate both branch
pathInfoMapNew.put(newPathInfoBean,currentPath+pathCount);
}
}
}
return findNumberOfSequence1(pathInfoMapNew,sequenceLen+1,lenOfSequence);
}
public static void main(String[] args) {
KnightMoveMain km= new KnightMoveMain(2);
long now = System.currentTimeMillis();
for (int n=0;n<=32;n++) {
long numberOfPaths = km.findNumberOfSequence(n);
System.out.println(n+"==>"+numberOfPaths);
}
long elapsedTime = System.currentTimeMillis()-now;
System.out.println("Total Elapsed Time (msec):"+elapsedTime);
}
}
//###############################################################
//###############################################################
//###############################################################
package com.denizstij.km;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
/**
*
* @author Deniz Turan, http://denizstij.blogspot.co.uk denizstij AT gmail.com
*
*/
public class Keyboard{
final private Set <key> KEYSET= new LinkedHashSet<key>();
final public static Key A= new Key('A', true);
final public static Key B= new Key('B', false);
final public static Key C= new Key('C', false);
final public static Key D= new Key('D', false);
final public static Key E= new Key('E', true);
final public static Key F= new Key('F', false);
final public static Key G= new Key('G', false);
final public static Key H= new Key('H', false);
final public static Key I= new Key('I', true);
final public static Key J= new Key('J', false);
final public static Key K= new Key('K', false);
final public static Key L= new Key('L', false);
final public static Key M= new Key('M', false);
final public static Key N= new Key('N', false);
final public static Key O= new Key('O', true);
final public static Key _1= new Key('1', false);
final public static Key _2= new Key('2', false);
final public static Key _3= new Key('3', false);
final public static Keyboard INSTANCE= new Keyboard();
private void init(){
KEYSET.add(A.setKnightMoves(H,L));
KEYSET.add(B.setKnightMoves(I,K,M));
KEYSET.add(C.setKnightMoves(F,J,L,N));
KEYSET.add(D.setKnightMoves(G,M,O));
KEYSET.add(E.setKnightMoves(H,N));
KEYSET.add(F.setKnightMoves(C,M,_1));
KEYSET.add(G.setKnightMoves(D,N,_2));
KEYSET.add(H.setKnightMoves(A,E,K,O,_1,_3));
KEYSET.add(I.setKnightMoves(B,L,_2));
KEYSET.add(J.setKnightMoves(C,M,_3));
KEYSET.add(K.setKnightMoves(B,H,_2));
KEYSET.add(L.setKnightMoves(A,C,I,_3));
KEYSET.add(M.setKnightMoves(B,D,F,J));
KEYSET.add(N.setKnightMoves(C,E,G,_1));
KEYSET.add(O.setKnightMoves(D,H,_2));
KEYSET.add(_1.setKnightMoves(F,H,N));
KEYSET.add(_2.setKnightMoves(G,I,K,O));
KEYSET.add(_3.setKnightMoves(H,J,L));
}
// Singleton
private Keyboard(){
init();
}
public Set <key> getKeySet() {
return KEYSET;
}
public static class Key{
final private char key;
final private boolean isVowel;
private Set<key> knightMoves;
Key(char key, boolean isVowel){
this.key=key;
this.isVowel=isVowel;
}
public Key setKnightMoves(Key ...knightMoves) {
this.knightMoves=Collections.unmodifiableSet(new HashSet<key>(Arrays.asList(knightMoves)));
return this;
}
public Set<key> getKnightMoves() {
return knightMoves;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + key;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Key other = (Key) obj;
if (key != other.key)
return false;
return true;
}
public char getKey() {
return key;
}
public boolean isVowel() {
return isVowel;
}
@Override
public String toString() {
return "Key [key=" + key + ", isVowel=" + isVowel +"]";
}
}
}
//###############################################################
//###############################################################
//###############################################################
package com.denizstij.km;
import com.denizstij.km.Keyboard.Key;
/**
*
* @author Deniz Turan, http://denizstij.blogspot.co.uk denizstij AT gmail.com
*
*/
public class PathInfoBean {
final private Key key;
final private int vowelCount;
public PathInfoBean(Key key, int vowelCount) {
this.key = key;
this.vowelCount = vowelCount;
}
public Key getKey() {
return key;
}
public int getVowelCount() {
return vowelCount;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + vowelCount;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PathInfoBean other = (PathInfoBean) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
if (vowelCount != other.vowelCount)
return false;
return true;
}
@Override
public String toString() {
return "PathInfoBean [key=" + key + ", vowelCount=" + vowelCount + "]\n";
}
}
Output
1==>18 2==>60 3==>214 4==>732 5==>2486 6==>8392 7==>27912 8==>93204 9==>306288 10==>1013398 11==>3302640 12==>10842528 13==>35108325 14==>114492546 15==>368772182 16==>1195650888 17==>3833937659 18==>12367469662 19==>39505386940 20==>126863960276 21==>403894417802 22==>1291845025388 23==>4100838894764 24==>13069438426724 25==>41380854190503 26==>131455706155352 27==>415265140628246 28==>1315326269976050 29==>4146551571701468 30==>13098978945964762 31==>41218021719932582 32==>129891093550589788 Total Elapsed Time (msec):450

