I'm making a game and want to implement cheat codes like the Konami code.
But how do I check for that sequence of keystrokes?
I want it to work so that if a player just types the code it will trigger.
Thanks in advance!
Below is a class that checks for the Konami code, including cases such as "UP, UP, UP, DOWN, etc."
This should work for any given sequence.
import java.util.Map;
import java.util.TreeMap;
public class Konami {
static private int[] code =
{UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B};
static private Map<Integer, Integer>[] graph;
static private int currentNode = 0;
public static void main(String args[]) {
//Create graph
graph = generateSequenceMap(code);
//Call checkKonami(key) whenever a key is pressed
}
static public boolean checkKonami(int keyPressed) {
Integer nextNode = graph[currentNode].get(keyPressed);
//Set currentNode to nextNode or to 0 if no matching sub-sequence exists
currentNode = (nextNode==null ? 0 : nextNode);
return currentNode == code.length-1;
}
static private Map<Integer, Integer>[] generateSequenceMap(int[] sequence) {
//Create map
Map<Integer, Integer>[] graph = new Map[sequence.length];
for(int i=0 ; i<sequence.length ; i++) {
graph[i] = new TreeMap<Integer,Integer>();
}
//i is delta
for(int i=0 ; i<sequence.length ; i++) {
loop: for(int j=i ; j<sequence.length-1 ; j++) {
if(sequence[j-i] == sequence[j]) {
System.out.println("If at Node "+j+" you give me seq["+(j-i+1)
+ "] OR " + (sequence[j-i+1]) + " , goto Node " + (j-i+1));
//Ensure that the longest possible sub-sequence is recognized
Integer value = graph[j].get(sequence[j-i+1]);
if(value == null || value < j-i+1)
graph[j].put(sequence[j-i+1], j-i+1);
}
else
break loop;
}
}
return graph;
}
}
EDIT:
See my other post for code that always works. The following doesn't detect the code if it overlaps with itself (for instance: "UP, UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B" wouldn't work)
Thanks to Gevorg for pointing this out.
If it's how to identify the sequence that you are concerned with only (I'll assume you know how to get input from the keyboard) then you can have something as follows.
int[] sequence = {UP, UP, DOWN, DOWN, LEFT, RIGHT, LEFT, RIGHT, B};
int currentButton = 0;
boolean checkKonami(int keyPressed) {
//Key sequence pressed is correct thus far
if(keyPressed == sequence[currentButton]) {
currentButton++;
//return true when last button is pressed
if(currentButton == sequence.length) {
//Important! Next call to checkKonami()
//would result in ArrayIndexOutOfBoundsException otherwise
currentButton = 0;
return true;
}
}
else {
//Reset currentButton
currentButton = 0;
}
return false;
}
Call this function whenever a key press is registered, passing the key that has been pressed. Of course modify the types where appropriate.
I'm sure you're past this project now, but I just implemented this into one of my assignments and wanted to leave it for others to find. This solution logs the last n keystrokes (defined here as 10) into a circular array and returns true when they match our code. You could just as easily pass in different lengths and codes as part of the method (but this implementation didn't require it). I used ^ ^ v v < > < > b a.
public class Code {
private static int[] history = new int[10];
private static int front = 0;
private static int size = 0;
// Here is the Code they must enter (ascii vals for konami).
private static int[] code = {38, 38, 40, 40, 37, 39, 37, 39, 66, 65};
// Static class. No constructor.
private Code(){}
// Adds key-press into history buffer. If code is matched, return true.
public static boolean add(int e){
// Write the value into our key history.
history[(front + size) % 10] = e;
// Stop growing at length 10 and overwrite the oldest value instead.
if (size < 10){
size++;
} else {
front = front + 1 % 10;
}
// Compare our history (from the current front) to the code (from 0)
for(int i = front; i < front + size; i++){
if (history[i % 10] != code[i-front]){
// Any 1 mismatch will abort
return false;
}
}
// Make sure we've logged enough keystrokes so it doesn't fire off
// if your first key press matches the code.
if (size < 10){
return false;
}
return true;
}
Enjoy! :D
I don't know what your needs are. Are you trying to create a game using System.in and System.out or are you trying to make a full visual GUI?
In the meantime, see interface java.awt.event.KeyListener. (Oracle Documentation) Also, see Oracle's Tutorial.
And based on personal experience, the code below approximates what you need.
import java.awt.event.*; //Specifically KeyListener and KeyEvent
import java.util.ArrayList;
public class Test implements KeyListener {
private final int[] cheatCode = {38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 83, 84, 65, 82, 84} //assuming user types out "start"
private final ArrayList<Integer> KONAMI_CODE = createCheatCode(cheatCode);
private ArrayList<Integer> typedKeys = new ArrayList<Integer>();
public Test() {
//constructor goes here, if necessary
}
public /*static*/ ArrayList<Integer> createCheatCode(int[] code) { //uses Key Codes
ArrayList<Integer> temp = new ArrayList<Integer>();
for (int i = 0; i < code.length; i++)
temp.add(new Integer(code[i]));
return temp;
}
// Warning: MUST implement ALL KeyListener methods, or compiler will complain
public /*static*/ void keyPressed(KeyEvent e) {}
public /*static*/ void keyReleased(KeyEvent e) {
typedKeys.add(new Integer(e.getKeyCode()));
}
public /*static*/ void keyTyped(KeyEvent e) {}
public /*static*/ boolean cheatEntered() {
int cheatLen = KONAMI_CODE.size(); // or length, depending on what you use
int index = typedKeys.size() - cheatLen;
if (index < 0)
return false;
return typedKeys.get(index, typedKeys.size()).equals(KONAMI_CODE);
}
}
When using a runner method, just specify that
if (test.cheatEntered()) {
// do something
}
You can remove the /*static*/ if you want object-oriented programming; otherwise, get rid of the /* and */ pairs if you want to run it using static methods.
It might be interesting to look at the state pattern but you might try the trick below since this is a simple case:
Put the sequence to be recognized in a String secretCode
Create a StringBuilder userInput that will hold the keys pressed by the user
Every time a user presses a key append it to userInput
After each key that is appended in userInput check if the updated userInput contains the secretCode withthe following: userInput.indexOf(secretCode)>-1
You might want to empty the userInput from now and then based on time or after a sequence is recognized for instance.
why not just use string.contains?
public class Konami {
private static final String KONAMI_SEQUENCE = "↑↑↓↓←→←→BA";
StringBuilder sb = new StringBuilder();
public void character(char c) {
sb.append(c);
}
public boolean isKonami() {
return sb.toString().contains(KONAMI_SEQUENCE);
}
public static void main(String[] args) {
Konami konami = new Konami();
String str1 = "↑↑↓↓←→←→BA";
String str2 = "↑↑↑↑↓↓←→←→BA";
String str3 = "↑↑↓↓→↓←←→←→BA";
for(int i = 0 ; i < str3.length(); i ++) {
konami.character(str3.charAt(i));
}
System.out.println(konami.isKonami());
}
}
Related
I was asked to program a method that receives a scanner, and returns a sorted array of words which contain only letters, with no repetitions (and no bigger in length than 3000). Then, I was asked to program a method that checks whether a certain given string is contained in a given vocabulary. I used a simple binary search method.
This is what I've done:
public static String[] scanVocabulary(Scanner scanner){
String[] array= new String[3000];
int i=0;
String word;
while (scanner.hasNext() && i<3000) {
word=scanner.next();
if (word.matches("[a-zA-Z]+")){
array[i]=word.toLowerCase();
i++;
}
}int size=0;
while (size<3000 && array[size]!=null ) {
size++;
}
String[] words=Arrays.copyOf(array, size);
if (words.length==0 || words.length==1) {
return words;
}
else {
Arrays.sort(words);
int end= removeDuplicatesSortedArr(words);
return Arrays.copyOf(words, end);
}
}
private static int removeDuplicatesSortedArr(String[] array) { //must be a sorted array. returns size of the new array
int n= array.length;
int j=0;
for (int i=0; i<n-1; i++) {
if (!array[i].equals(array[i+1])) {
array[j++]=array[i];
}
}
array[j++]=array[n-1];
return j;
}
public static boolean isInVocabulary(String[] vocabulary, String word){
//binary search
int n=vocabulary.length;
int left= 0;
int right=n-1;
while (left<=right) {
int mid=(left+right)/2;
if (vocabulary[mid].equals(word)){
return true;
}
else if (vocabulary[mid].compareTo(word)>0) {
right=mid-1;
}else {
right=mid+1;
}
}
return false;
}
while trying the following code:
public static void main(String[] args) {
String vocabularyText = "I look at the floor and I see it needs sweeping while my guitar gently weeps";
Scanner vocabularyScanner = new Scanner(vocabularyText);
String[] vocabulary = scanVocabulary(vocabularyScanner);
System.out.println(Arrays.toString(vocabulary));
boolean t=isInVocabulary(vocabulary, "while");
System.out.println(t);
System.out.println("123");
}
I get nothing but-
[and, at, floor, gently, guitar, i, it, look, my, needs, see, sweeping, the, weeps, while]
nothing else is printed out nor returned. Both functions seem to be working fine separately, so I don't get what I'm doing wrong.
I would be very happy to hear your thoughts, thanks in advance :)
This has nothing to do with the console. Your isInVocabulary method is entering an infinite loop in this block:
if (!isInVocabulary(vocabulary, "while")) {
System.out.println("Error");
}
If you were to debug through isInVocabulary, you would see that after a few iterations of the while loop,
left = 0;
right = 2;
mid = 1;
if (vocabulary[mid].equals(word)){
// it doesn't
} else if (vocabulary[mid].compareTo("while") > 0) {
// it doesn't
} else {
right = mid + 1;
// this is the same as saying right = 1 + 1, i.e. 2
}
So you'll loop forever.
I am writing a stack data structure in java using arrays. The problem is when I try to push the users char input it doesn't display. The problem is with this code.
public static void preSuf(Stack stack) {
Scanner key = new Scanner(System.in);
System.out.println("enter the values");
while(key.hasNext()){
char c = key.next().charAt(0);
stack.push(c);
}
}
When I change the while(key.hasNext()) to if(key.hasNext()) it works but it only prints one time and doesnt itterate. How can I fix this problem thank you.
Edit: Here is the whole code
import java.util.Scanner;
public class Stack {
private int top;
private char[] container;
private int size;
public static int pos = 0;
// constructor
public Stack(int N) {
container = new char[N];
size = N;
top = 0;
}
public boolean isFull() {
return (size == top);
}
public void push(char string) {
if (!isFull()) {
container[top] = string;
top++;
} else {
return;
}
}
public int pop() {
int drop;
drop = container[top - 1];
container[top] = 0;
top--;
return drop;
}
public int peek() {
int drop2;
drop2 = container[top - 1];
return drop2;
}
public void display() {
for (int i = 0; i < container.length; i++) {
if (container[i] != 0) {
System.out.print(container[i]);
}
}
}
public static void preSuf(Stack stack) {
Scanner key = new Scanner(System.in);
System.out.println("enter the values");
while(key.hasNext()){
char c = key.next().charAt(0);
stack.push(c);
}
}
public static void main(String args[]) {
Stack stack = new Stack(3);
preSuf(stack);
stack.display();
stack.display();
System.out.println();
}
}
The problem is that you haven't written any code to actually print the contents of your stack.
You could write a loop after your while loop to iterate over the stack and print out each character.
You'll also need a way of exiting your while loop. You can do this either with a special character, eg. if(c == '.') break; or you can just press Ctrl+Z.
EDIT: Based on the edit to the question and the full code being presented, I think the suggestion of needing the extra loop is now redundant. You have that in stack.display(). You just need to get out of your while loop.
you haven't determined when the loop should end.
you'd think if you press enter without entering anything the loop
would break but that's not how next operates. if you press enter
without entering anything or input data which consists of only whitespaces, next will block while waiting for input to
scan, even if a previous invocation of hasNext() returned true.
the solution is to include a condition at which control should break out of the loop.
So I have my equals method here in the class below, and I doesn't seem to be returning true when the if statement is true! I can't seem to find out why...
public class Player {
private int[] anyplayer = new int[5];
// constructor for each player at the table
public Player(int[] anyplayer) {
this.anyplayer = anyplayer;
}
// checks if the player has a winning bet in the european roulette
public boolean europeanequals(){
boolean truth = false;
for (int i = 0; i < anyplayer.length; i++) {
if (roulettedriver.eurowinningnumber == anyplayer[i]) {
truth = true;}
else {
truth = false;
}
}
return truth;
}
Here is my driver where I call the method:
public class roulettedriver {
// declaring the two roulettes
final static int[] europeanroulettegame = {0,32,15,19,4,21,2,25,17,34,6,27,13,36,11,30,8,23,10,5,24,16,33,1,20,14,31,9,22,18,29,7,28,12,35,3,26};
final static int[] americanroulettegame = {0,28,9,26,30,11,7,20,32,17,5,22,34,15,3,24,36,13,1,00,27,10,25,29,12,8,19,31,18,6,21,33,16,4,23,35,14,2};
// declaring the two winning numbers
public static int eurowinningnumber = europeanroulette.getRandom(europeanroulettegame);
public static int uswinningnumber = americanroulette.getRandom(americanroulettegame);
public static void main(String[] args) {
Scanner keyin = new Scanner(System.in);
// initializing the six players (First player)
int[] player1 = {-1,-1,-1,-1,-1}; // the numbers are set to -1 because 0 is a winning number
Player first_player = new vipplayer(player1); // First player is automatically a VIP
try{
for(int i=0;i<=5;i++) {
player1[i] = Integer.parseInt(keyin.nextLine());
}
}
catch(NumberFormatException e){
System.out.println("Player 2 : ");
}
// booleans are set to true if the bets match the randomly generated number
boolean winbet1 = first_player.europeanequals();
And basically my equals isn't comparing the right values I think... Can't seem to make this work? Any input? The value is supposed to be returned in the boolean winbet1
Your code continues even after finding a match, potentially resetting truth back to false, and returning that.
You need to change the method like so:
public boolean europeanequals(){
for (int i = 0; i < anyplayer.length; i++) {
if (roulettedriver.eurowinningnumber == anyplayer[i]) {
return true;
}
}
return false;
}
or using a for-each loop:
public boolean europeanequals(){
for (int number : anyplayer) {
if (roulettedriver.eurowinningnumber == number) {
return true;
}
}
return false;
}
While other answered, I want to add few notes:
arrays are zero-based in Java, this code:
for(int i=0;i<=5;i++) {
player1[i] = Integer.parseInt(keyin.nextLine());
}
will throw an exception, you should loop until 4 (or better, plater1.length)
follow Java Naming Conventions and rename your class to begin with upper case, also try to name your variables like firstPlayer instead of first_player
indent your code for a better and safer world
You probably forgot a break:
if (roulettedriver.eurowinningnumber == anyplayer[i]) {
truth = true;
break; // here
}
Without it, the loop will continue and probably set truth back to false
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
Here is my example - pick random number from 1-20, then from 2-21, 3-22 and so on, while
excluding previous picks. I am new to Java, and doing something wrong.
import java.util.Random;
import java.util.ArrayList;
import java.util.List;
public class RandomGenerator {
static int temp;
public static void main(String[] args) {
List<Integer> randomNumberArray = new ArrayList<Integer>();
Random RandomNumber = new Random();
for (int i = 0; i<=20; i++)
{
temp = RandomNumber.nextInt(i+20) +1+i;
if (!randomNumberArray.contains(temp))
{
{
randomNumberArray.add(temp);
}
}
System.out.println(randomNumberArray);
}
There are a couple of things to go over.
1) If your number sees that there is a duplicate then it will skip it and continue with the next number, for example if you run it 5 times and it finds a duplicate once, then the resulting list of numbers will have only 4 numbers not 5! since it skipped one. (Not sure if this is what you wanted or not)
2) Your randomly generated number doesn't grow the way you expect it to grow.
For example: At the sixth iteration over the loop your random number will generate as:
RandomNumber.nextInt(25) +6;
That means that the number range there isn't 6-26 but 6-30!
Because: nextInt will return an int between 0 and 24 and then that int is added another 6 to it.
EDIT :
To tackle your first problem you can continue to generate numbers until you generate one that is not a duplicate for that single cycle of the for loop.
For this you can utilize the do-while loop so that it executes the number generation at least once before checking whether the number is a duplicate within the for loop.
So you can adjust your for loop from:
for (int i = 0; i<=20; i++)
{
temp = RandomNumber.nextInt(20) +1+i;
if (!randomNumberArray.contains(temp))
{
{
randomNumberArray.add(temp);
}
}
System.out.println(randomNumberArray);
}
into:
for (int i = 0; i<=20; i++)
{
do {
temp = RandomNumber.nextInt(20) +1+i;
} while (randomNumberArray.contains(temp));
randomNumberArray.add(temp);
System.out.println(randomNumberArray);
}
Notice that the check in the while expression is the opposite (does not have the exclamation mark) of what was in the if expression in the for loop before, since we do want to continue generating new random numbers while our numbers are duplicates.
And since we are still looping within that one cycle of the for loop, it will always generate the number with the appropriate value of i which was set for that for cycle.
You aren't excluding previous picks. Something like this will print twenty random numbers (e.g. all random numbers from 1-20).
public static void main(String[] args) {
java.util.Set<Integer> picked = new TreeSet<Integer>();
Random rand = new Random();
while (picked.size() < 20) {
int temp = 1+rand.nextInt(20);
if (picked.contains(temp)) {
continue;
}
picked.add(temp);
System.out.println(temp);
}
}
I'm not sure I understand your "stepping" idea, but add this for temp and it will do that too -
int temp = 1+picked.size()+rand.nextInt(20+picked.size());
Since the range of allowed elements isn't too big, you can also hold the pool of all possible numbers and pick one of them. You can use e.g. RandomSet from this answer.
import java.util.*;
import java.lang.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
static class RandomSet<E> extends AbstractSet<E> {
List<E> dta = new ArrayList<E>();
Map<E, Integer> idx = new HashMap<E, Integer>();
public RandomSet() {
}
public RandomSet(Collection<E> items) {
for (E item : items) {
idx.put(item, dta.size());
dta.add(item);
}
}
#Override
public boolean add(E item) {
if (idx.containsKey(item)) {
return false;
}
idx.put(item, dta.size());
dta.add(item);
return true;
}
/**
* Override element at position <code>id</code> with last element.
* #param id
*/
public E removeAt(int id) {
if (id >= dta.size()) {
return null;
}
E res = dta.get(id);
idx.remove(res);
E last = dta.remove(dta.size() - 1);
// skip filling the hole if last is removed
if (id < dta.size()) {
idx.put(last, id);
dta.set(id, last);
}
return res;
}
#Override
public boolean remove(Object item) {
#SuppressWarnings(value = "element-type-mismatch")
Integer id = idx.get(item);
if (id == null) {
return false;
}
removeAt(id);
return true;
}
public E get(int i) {
return dta.get(i);
}
public E pollRandom(Random rnd) {
if (dta.isEmpty()) {
return null;
}
int id = rnd.nextInt(dta.size());
return removeAt(id);
}
#Override
public int size() {
return dta.size();
}
#Override
public Iterator<E> iterator() {
return dta.iterator();
}
}
public static void main (String[] args) throws java.lang.Exception
{
RandomSet<Integer> rs = new RandomSet<Integer>();
for (int i = 0; i < 20; ++i) {
rs.add(i);
}
int count = 50;
Random r = new Random();
for (int i = 0; i < count; i++) {
System.out.println(rs.pollRandom(r));
rs.remove(i);
rs.add(i + 20);
}
}
}
Using the smart structure the overall time complexity is O(N + K), where N is the number of requested polls and K is the size of the pool.
Running Ideone example : http://ideone.com/Sfltr7
The trick here is to use Collections.shuffle(List list):
List<Integer> list = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
Collections.shuffle(list);
System.out.println(list);
The progressive version goes something like this:
// Wrap it in an ArrayList so I can modify it.
List<Integer> list = new ArrayList(Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20));
for (int i = 21; i < 25; i++) {
System.out.println(list);
// Shuffle it.
Collections.shuffle(list);
// Grab one.
Integer take = list.get(0);
list.remove(take);
System.out.println("Took " + take);
// Add my new candidate.
list.add(i);
}
Or you could go the whole hog and make it an Iterable:
public static class CreepingRandom implements Iterable<Integer> {
// The starting list.
private final List<Integer> start;
// How many steps to add.
private final int steps;
// What int to start adding.
private final int from;
public CreepingRandom(int initialSize, int from, int steps) {
// Make my start list.
start = new ArrayList<Integer>(initialSize);
// Fill it.
for (int i = 1; i <= initialSize; i++) {
start.add(i);
}
// Remember where to start from.
this.from = from;
// Remember how many steps.
this.steps = steps;
}
#Override
public Iterator<Integer> iterator() {
return new CreepingIterator();
}
private class CreepingIterator implements Iterator<Integer> {
// Track how many I've delivered.
int delivered = 0;
// The next number to add.
int add = from;
// My current list.
final ArrayList<Integer> list = new ArrayList(start);
#Override
public boolean hasNext() {
return delivered < steps;
}
#Override
public Integer next() {
// Shuffle it.
Collections.shuffle(list);
// Pull one out.
Integer next = list.get(0);
// Add my new one in.
list.set(0, add++);
// Count them.
delivered += 1;
return next;
}
}
}
public void test() {
for (Integer i : new CreepingRandom(20, 21, 100)) {
System.out.println(i);
}
}
private class CreepingIterator implements Iterator<Integer> {
// Track how many I've delivered.
int delivered = 0;
// The next number to add.
int add = from;
// My current list.
final ArrayList<Integer> list;
CreepingIterator() {
// Copy the start list - Use LinkedList for efficiency of add and removeFirst.
list = new ArrayList(start);
}
#Override
public boolean hasNext() {
return delivered < steps;
}
#Override
public Integer next() {
// Shuffle it.
Collections.shuffle(list);
// Pull one out.
Integer next = list.get(0);
// Add my new one in.
list.set(0, add++);
// Count them.
delivered += 1;
return next;
}
}
}
public void test() {
for (Integer i : new CreepingRandom(20, 21, 100)) {
System.out.println(i);
}
}
I'm familiar with Levenshtein's distance, so I decided I would use it to solve UVA's Edit Steps Ladder problem.
My solution is:
import java.io.*;
import java.util.*;
class LevenshteinParaElJuez implements Runnable{
static String ReadLn(int maxLength){ // utility function to read from stdin,
// Provided by Programming-challenges, edit for style only
byte line[] = new byte [maxLength];
int length = 0;
int input = -1;
try{
while (length < maxLength){//Read untill maxlength
input = System.in.read();
if ((input < 0) || (input == '\n')) break; //or untill end of line ninput
line [length++] += input;
}
if ((input < 0) && (length == 0)) return null; // eof
return new String(line, 0, length);
}catch (IOException e){
return null;
}
}
public static void main(String args[]) // entry point from OS
{
LevenshteinParaElJuez myWork = new LevenshteinParaElJuez(); // Construct the bootloader
myWork.run(); // execute
}
public void run() {
new myStuff().run();
}
}
class myStuff implements Runnable{
public void run(){
ArrayList<String> theWords = new ArrayList<String>();
try
{
/// PLACE YOUR JAVA CODE HERE
String leido=LevenshteinParaElJuez.ReadLn(100);
//System.out.println("lo leido fue "+leido);
while (leido.length() != 0){
theWords.add(leido);
leido=LevenshteinParaElJuez.ReadLn(100);
}
}catch(Exception e){
System.out.println("El programa genero una excepcion");
}
int maxEdit=0;
int actualEdit=0;
int wordsIndex1 =0, wordsIndex2=0;
while (wordsIndex1<= theWords.size())
{
while (wordsIndex2<= theWords.size()-1){
actualEdit=Levenshtein.computeLevenshteinDistance(theWords.get(wordsIndex1),theWords.get(wordsIndex2));
if (actualEdit>maxEdit){maxEdit=actualEdit;}
wordsIndex2++;
}
wordsIndex1++;
}
System.out.println(maxEdit+1);
}
}
class Levenshtein {
private static int minimum(int a, int b, int c) {
if(a<=b && a<=c)
return a;
if(b<=a && b<=c)
return b;
return c;
}
public static int computeLevenshteinDistance(String str1, String str2) {
return computeLevenshteinDistance(str1.toCharArray(),
str2.toCharArray());
}
private static int computeLevenshteinDistance(char [] str1, char [] str2) {
int [][]distance = new int[str1.length+1][str2.length+1];
for(int i=0;i<=str1.length;i++)
distance[i][0]=i;
for(int j=0;j<=str2.length;j++)
distance[0][j]=j;
for(int i=1;i<=str1.length;i++)
for(int j=1;j<=str2.length;j++)
distance[i][j]= minimum(distance[i-1][j]+1,
distance[i][j-1]+1,
distance[i-1][j-1]+
((str1[i-1]==str2[j-1])?0:1));
return distance[str1.length][str2.length];
}
}
With this input:
cat
dig
dog
fig
fin
fine
fog
log
wine
it produces the correct output for this sample:
5
The judge is rejecting my answer. This is my first attempt at solving an online judge's problem, and I think I maybe forcing a correct answer here:
System.out.println(maxEdit+1);
since maxEdit has a value of 4 when computed simply with Levenshtein. Is that what's going on?
Levinshtein is relevant, but will not give you a value used in your output. In this problem, use it to determine if two words have an edit distance of exactly 1, indicating the two words compared are adjacent in the edit step ladder.
Iterate over the words in the dict. and if the next word has an edit distance of 1 from the current word, you may make that the current word, otherwise it must be skipped.
The trick to this problem is finding all possible sequences - just because the next word has an edit distance of 1 doesn't mean using it in the ladder will give you the longest possible ladder.
The problem states that you are to find the longest lexicographically ordered (i.e. alphabetical) sequence in the dictionary, such that each word in the sequence is formed by adding, deleting, or changing one letter.
So the 5 in the sample result is for the sequence (dig, fig, fin, fine, wine).
I don't think Levenshtein is particularly relevant to this problem, though maybe I am just not imaginative enough. Levenshtein doesn't capture the requirement that each step must be in the dictionary, and later in the dictionary.