Get next run from a text file - java

I am trying to write a function in Java that returns the next ascending sequence (run) from a txt file, let's say the return type of a function would be ArrayList.
My example file input.txt contains next values: 78123421. So in terms of runs that means the file has 4 runs: |78|1234|2|1|.
What am I trying to reach here is like when I would call this function from main() four times it should print something like
1.run: 78,
2.run: 1234,
3.run: 2,
4.run: 1
or just two calls should print
1.run: 78,
2.run: 1234
I have tryed to solve my problem using BufferedReader/FileReader and RandomAccessFile but no working solution so far, please help :/
So this is what I have so far. The main idea was to use RandomAccessFile and read from input as long as run condition is satisfied. But the reader reads one value more, that is why I use seek() to start reading at the right position when next function call happens. There must be a bug in the code, because it doesn't print all the runs or just an Exception fires.
import java.io.File;
import java.util.ArrayList;
import java.io.RandomAccessFile;
public class GetRunsFromFile
{
static long start = 0;
static long read_len = 0;
public static void main(String[] args) throws Exception
{
File in = new File("C:/Users/henrich/Desktop/Gimp.txt");
RandomAccessFile raf = new RandomAccessFile(in,"r");
ArrayList<Integer> current_run = new ArrayList<Integer>();
for(int i=1;i<=4;i++)
{
current_run = getNextRun(raf);
printArrayList(current_run);
}
raf.close();
}
private static ArrayList<Integer> getNextRun(RandomAccessFile raf) throws Exception
{
int v;
String line;
int val = Integer.MIN_VALUE;
ArrayList<Integer> run = new ArrayList<Integer>();
while((line=raf.readLine())!= null)
{
v = Integer.parseInt(line.trim());
if(v >= val)
{
read_len = raf.getFilePointer() - start;
start = raf.getFilePointer();
run.add(v);
val = v;
}
else
{
raf.seek(raf.getFilePointer() - read_len);
start = raf.getFilePointer();
return run;
}
}
return null;
}
private static void printArrayList(ArrayList<Integer> al)
{
for(int i=0; i<al.size(); i++)
{
System.out.print(al.get(i) + " ");
}
System.out.println();
System.out.println("------");
}
}
For more questions please let me know.
Note: It should work only for ascending runs and files of any length.
Thanks for the support.

There are several ways to do it.
solution 1
For instance call your function with an int and make it return an int refering to the number of the last printed char.
Run Exaple:
after the first run return 2 cause the length of print text is 2
after the second run return 6 cause the length of print text is 4 +2 from last loop... etc.
public int function(int startPoint){
// do stuff here
return lastIndexofPrintChar;
}
then call your function like this
loop{
int result=0;
result= function(x);
}
solution 2
You can also dublicate your file and remove every String you print.

private static void getNextRun()
{
try
{
BufferedReader br = new BufferedReader(new FileReader(new File("C:/Users/henrich/Desktop/Gimp.txt")));
br.skip(skip_lines);
int v;
String line;
int val = Integer.MIN_VALUE;
ArrayList<Integer> al = new ArrayList<Integer>();
while((line=br.readLine())!= null)
{
skip_lines += line.length()+2;
v = Integer.parseInt(line.trim());
if(v >= val)
{
al.add(v);
val = v;
}
else
{
skip_lines -= line.length() + 2;
printArrayList(al);
break;
}
}
br.close();
}
catch (Exception e){System.out.println("EOF");}
}

Related

How to convert ArrayList<String> to int[] in Java

I read Bert Bates and Katie Sierra's book Java and have a problem.
The Task: to make the game "Battleship" with 3 classes via using ArrayList.
Error: the method setLocationCells(ArrayList < String >) in the type
SimpleDotCom is not applicable for the arguments (int[])
I understand that ArrayList only will hold objects and never primatives. So handing over the list of locations (which are int's) to the ArrayList won't work because they are primatives. But how can I fix it?
Code:
public class SimpleDotComTestDrive {
public static void main(String[] args) {
int numOfGuesses = 0;
GameHelper helper = new GameHelper();
SimpleDotCom theDotCom = new SimpleDotCom();
int randomNum = (int) (Math.random() * 5);
int[] locations = {randomNum, randomNum+1, randomNum+2};
theDotCom.setLocationCells(locations);
boolean isAlive = true;
while(isAlive) {
String guess = helper.getUserInput("Enter the number");
String result = theDotCom.checkYourself(guess);
numOfGuesses++;
if (result.equals("Kill")) {
isAlive = false;
System.out.println("You took " + numOfGuesses + " guesses");
}
}
}
}
public class SimpleDotCom {
private ArrayList<String> locationCells;
public void setLocationCells(ArrayList<String> loc) {
locationCells = loc;
}
public String checkYourself(String stringGuess) {
String result = "Miss";
int index = locationCells.indexOf(stringGuess);
if (index >= 0) {
locationCells.remove(index);
if(locationCells.isEmpty()) {
result = "Kill";
} else {
result = "Hit";
}
}
return result;
}
}
public class GameHelper {
public String getUserInput(String prompt) {
String inputLine = null;
System.out.print(prompt + " ");
try {
BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
inputLine = is.readLine();
if (inputLine.length() == 0)
return null;
} catch (IOException e) {
System.out.println("IOException:" + e);
}
return inputLine;
}
}
convert ArrayList to int[] in Java
Reason for Basic Solution
Here's a simple example of converting ArrayList<String> to int[] in Java. I think it's better to give you an example not specific to your question, so you can observe the concept and learn.
Step by Step
If we have an ArrayList<String> defined below
List<String> numbersInAList = Arrays.asList("1", "2", "-3");
Then the easiest solution for a beginner would be to loop through each list item and add to a new array. This is because the elements of the list are type String, but you need type int.
We start by creating a new array of the same size as the List
int[] numbers = new int[numbersInAList.size()];
We then iterate through the list
for (int ndx = 0; ndx < numbersInAList.size(); ndx++) {
Then inside the loop we start by casting the String to int
int num = Integer.parseInt(numbersInAList.get(ndx));
But there's a problem. We don't always know the String will contain a numeric value. Integer.parseInt throws an exception for this reason, so we need to handle this case. For our example we'll just print a message and skip the value.
try {
int num = Integer.parseInt(numbersInAList.get(ndx));
} catch (NumberFormatException formatException) {
System.out.println("Oops, that's not a number");
}
We want this new num to be placed in an array, so we'll place it inside the array we defined
numbers[ndx] = num;
or combine the last two steps
numbers[ndx] = Integer.parseInt(numbersInAList.get(ndx));
Final Result
If we combine all of the code from "Step by Step", we get the following
List<String> numbersInAList = Arrays.asList("1", "2", "-3");
int[] numbers = new int[numbersInAList.size()];
for (int ndx = 0; ndx < numbersInAList.size(); ndx++) {
try {
numbers[ndx] = Integer.parseInt(numbersInAList.get(ndx));
} catch (NumberFormatException formatException) {
System.out.println("Oops, that's not a number");
}
}
Important Considerations
Note there are more elegant solutions, such as using Java 8 streams. Also, it's typically discouraged to store ints as Strings, but it can happen, such as reading input.
I can't see where you call setLocationCells(ArrayList<String>) in your code, but if the only problem is storing integers into an ArrayList there is a solution:
ArrayList<Integer> myArray = new ArrayList<Integer>();
myArray.add(1);
myArray.add(2);
It is true that you can't use primitive types as generics, but you can use the Java wrapper types (in this case, java.lang.Integer).

read lines from external file and store elements in array

I am new here so please show some patience. I am trying to read the data from an external file and store the info in 2 arrays.
The file looks like this:
0069 723.50
0085 1500.00
0091 8237.31
I am using 2 scanners to read the input and I think they work ok because when I try to print, the result looks ok.
My first problem is that I am able to read the first numbers on the list using nextInt(), but cannot use nextDouble() for the double ones as I get the "java.util.InputMismatchException" message. For that reason I read it as a String. The part with the other two scanners is supposed to do what the first parts should do, for a different input file, but the problem is the same.
My next and biggest problem, until now, is that am not able to store the values from the two columns in two distinct arrays. I have tried several ways (all commented) but all fail. Please help and thanks.
Here is my code:
import ui.UserInterfaceFactory;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Scanner;
import ui.UIAuxiliaryMethods;
public class Bank {
static final int MAX_NUMBER_OF_ACCOUNTS = 50;
PrintStream out;
Bank(){
UserInterfaceFactory.enableLowResolution(true);
out = new PrintStream(System.out);
}
void readFiles(){
Scanner balanceFile = UIAuxiliaryMethods.askUserForInput().getScanner();
while(balanceFile.hasNextLine()){
String balance_Line = balanceFile.nextLine();
Scanner accountsFile = new Scanner(balance_Line);
int account = accountsFile.nextInt(); //works
out.printf("%04d ",account);
/*int [] accounts_array = new int [MAX_NUMBER_OF_ACCOUNTS]; //does not store the values properly
int account = accountsFile.nextInt();
for(int j=0; j < accounts_array.length; j++){
accounts_array[j] = account;
}*/
/*int [] accounts_array = new int [MAX_NUMBER_OF_ACCOUNTS]; //java.util.InputMismatchException
for(int j=0; j < accounts_array.length; j++){
accounts_array[j] = accountsFile.nextInt();
//out.printf("%04d \n",accounts_array[j]);
}*/
String balance = accountsFile.nextLine(); //problem declaring balance as a double
out.printf("%s\n",balance);
/*String [] balance_array = new String [MAX_NUMBER_OF_ACCOUNTS]; //java.util.NoSuchElementException
for(int j=0; j < balance_array.length; j++){
accountsFile.useDelimiter(" ");
balance_array[j] = accountsFile.next();
//out.printf("%04d \n",accounts_array[j]);
}*/
}
Scanner mutationsFile = UIAuxiliaryMethods.askUserForInput().getScanner();
while(mutationsFile.hasNext()){
String mutation_Line = mutationsFile.nextLine();
Scanner mutatedAccountsFile = new Scanner(mutation_Line);
int mutated_account = mutatedAccountsFile.nextInt();
out.printf("%04d ",mutated_account);
int action = mutatedAccountsFile.nextInt(); //deposit or withdrawal
/*if (action == 1){
}else{
}*/
out.printf(" %d ",action);
/*Double amount = mutatedAccountsFile.nextDouble();
out.printf(" %5.2f ",amount);*/
String amount = mutatedAccountsFile.nextLine();
out.printf("%s\n",amount);
}
}
void start(){
new Bank();readFiles();
}
public static void main(String[] args) {
new Bank().start();
}
}
The InputMismatchException occurs because you try to read a double using the nextInt() function. To solve this issue, you can first read the tokens as Strings using the next() function and convert them appropriately.
while(mutationsFile.hasNext()){
mutation_Line = mutationsFile.next();
if(mutation_Line.indexOf(".") == -1)
//token is int
else
//token is double
}
Since you already know what the contents of the two columns are, you can store the integers and doubles in two lists and then, if you want, get them into an array.
List<Integer> intList = new ArrayList<Integer>();
List<Double> doubleList = new ArrayList<Double>();
Now replace the if statements in the first snippet with this:
if(mutation_Line.indexOf(".") == -1)
intList.add(new Integer(Integer.parseInt(mutation_Line)));
else
doubleList.add(new Double(Double.parseDouble(mutation_Line)));
In the end, you can get them into arrays:
Object[] intArr = intList.toArray(),
doubleArr = doubleList.toArray();
//display the contents:
for(int i=0; i<intArr.length; i++)
out.printf("%04d\t%.2f\n", Integer.parseInt(intArr[i].toString()),
Double.parseDouble(doubleArr[i].toString()));
OUTPUT:
0069 723.50
0085 1500.00
0091 8237.31
First off, you don't need to use 2 scanners. The Scanner object is simply reading your file, one scanner is plenty to accomplish the task of reading a file.
If you're trying to read the integers/doubles from file and are having trouble with nextInt() and nextDouble(), consider a different approach to parsing (e.g. parse the line into a string, split the line into 2 parts based on a space character, then trim both resulting strings and convert to respective integers/doubles).
Now back to the Scanner parsing the two values, remember first that when you use a next() or nextInt(), etc. those methods consume the next respective token. So parsing a line as a string from the file into another Scanner object is redundant and unnecessary in this case.
If you know your max number of accounts, and it's simply 50, then go ahead an allocate that prior to the while loop.
Here's an alternative approach with the code you posted.
public class App {
static int MAX_NUMBER_OF_ACCOUNTS = 50;
static PrintStream out;
static void readFiles() {
Scanner balanceFile = null;
try {
balanceFile = new Scanner(new File("C:\\Users\\Nick\\Desktop\\test.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (balanceFile == null)
return;
int [] accounts_array = new int [MAX_NUMBER_OF_ACCOUNTS];
double [] balance_array = new double [MAX_NUMBER_OF_ACCOUNTS];
int currentIndex = 0;
while (balanceFile.hasNextLine()) {
int account = balanceFile.nextInt();
double balance = balanceFile.nextDouble();
System.out.print("acc = " + account + " ");
System.out.println("bal = " + balance);
//out.printf("%04d ", account);
accounts_array[currentIndex] = account;
//out.printf("%s\n", balance);
balance_array[currentIndex] = balance;
currentIndex++;
}
balanceFile.close();
}
static void start() {
readFiles();
}
public static void main(String[] args) {
start();
}
}
Please note the excessive use of static could also be avoided in the future, but for the sake of the example it spread like the plague.
As you can see in the logic leading up to the while loop, the scanner object is made from a file I copied your example data into a file on my desktop. The arrays are allocated prior to the while loop (note: see #progy_rock and their use of ArrayList's - may help improve your code in the long run). And finally, note the index count to move the position along in the array to which you are inserting your lines to.

Filtering/sorting a collection through object fields?

I'm not sure why this isn't working. I'm not sure if it's a problem with the printing, or if it's a problem with the methods themselves.
I am making a program that takes a collection of songs and filters or sorts it according to a given user input. The user should be able to input multiple commands to further narrow down the list.
My filterRank and filterYear methods work perfectly fine, but the other methods end up printing a seemingly random selection of songs that do not change regardless of what is inputted as the title or artist to be filtered by, which generally appears only after an extremely long waiting period and a long series of spaces.
Even after this amalgam of songs is printed, the program does not terminate, and periodically outputs a space in the console, as in a System.out.println() statement were being continuously run.
If I remove the code that configures the output file, which is a requirement for the project, the methods fail to print entirely. Regardless of either of these changes, filterRank and filterYear continue to work perfectly.
This problem also occurs with my sort methods. No matter what sort method I run, it still prints out the spaces and the random songs, or nothing at all.
Is there something I'm missing? I've tried printing out variables and strategically inserting System.out.println("test") in my program to determine what the program is, but it seems as though it's parsing the input correctly, and the methods are indeed being successfully run.
I've been otherwise unable to isolate the problem.
Can I get assistance in determining what I'm missing? Despite poring over my code for two hours, I just can't figure out what the logical error on my part is.
Here is the relevant code:
The main class:
public static void main(String[] args) throws FileNotFoundException, IOException{
//user greeting statements and instructions
//scanning file, ArrayList declaration
Scanner input = new Scanner(System.in);
while (input.hasNextLine()) {
int n = 0;
SongCollection collection = new SongCollection(songs);
String inputType = input.nextLine();
String delims = "[ ]";
String[] tokens = inputType.split(delims);
for (int i = 0; i < tokens.length; i++) {
n = 0;
if (n == 0) {
if ((tokens[i]).contains("year:")) {
collection.filterYear(Range.parse(tokens[i]));
n = 1;
}// end of year loop
if ((tokens[i]).contains("rank:")) {
collection.filterRank(Range.parse(tokens[i]));
n = 1;
}// end of rank
if ((tokens[i]).contains("artist:")) {
collection.filterArtist(tokens[i]);
n = 1;
}// end of artist
if ((tokens[i]).contains("title:")) {
collection.filterTitle(tokens[i]);
n = 1;
}// end of title
if ((tokens[i]).contains("sort:")) {
if ((tokens[i]).contains("title")) {
collection.sortTitle();
n = 1;
}// end of sort title
if ((tokens[i]).contains("artist")) {
collection.sortArtist();
n = 1;
}// end of sort artist
if ((tokens[i]).contains("rank")) {
collection.sortRank();
n = 1;
}// end of sort rank
if ((tokens[i]).contains("year")) {
collection.sortYear();
n = 1;
}// end of sort year
}//end of sort
}// end of for loop
}// end of input.hasNextline loop
/*final PrintStream console = System.out; //saves original System.out
File outputFile = new File("output.txt"); //output file
PrintStream out = new PrintStream(new FileOutputStream(outputFile)); //new FileOutputStream
System.setOut(out); //changes where data will be printed
*/ System.out.println(collection.toString());
/*System.setOut(console); //changes output to print back to console
Scanner outputFileScanner = new Scanner(outputFile); //inputs data from file
while ((outputFileScanner.hasNextLine())) { //while the file still has data
System.out.println(outputFileScanner.nextLine()); //print
}
outputFileScanner.close();
out.close();*/
}
}// end of main
}// end of class
The SongCollection Class, with all of its respective filter and sort methods:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
public class SongCollection {
ArrayList<Song> songs2;
ArrayList<Song> itemsToRemove = new ArrayList<Song>(); // second collection
// for items to
// remove
public SongCollection(ArrayList<Song> songs) { // constructor for SongCollection
System.out.println("Test");
this.songs2 = songs;
}
public void filterYear(Range r) {
int n = 0;
if (n == 0) {
System.out.println("Program is processing.");
n++;
for (Song song1 : songs2) {
if (song1.year > (r.getMax()) || (song1.year) < (r.getMin())) {
itemsToRemove.add(song1);
}
}
songs2.removeAll(itemsToRemove);
itemsToRemove.clear();
}
}
public void filterRank(Range r) {
int n = 0;
if (n == 0) {
System.out.println("Program is processing.");
n++;
for (Song song1 : songs2) {
if (song1.rank > (r.getMax()) || (song1.rank) < (r.getMin())) {
itemsToRemove.add(song1);
}
}
songs2.removeAll(itemsToRemove);
itemsToRemove.clear();
}
}
public void filterArtist(String s) {
int n = 0;
if (n == 0) {
System.out.println("Program is processing.");
n++;
for (Song song1 : songs2) {
if ((!(((song1.artist).contains(s))))) {
itemsToRemove.add(song1);
}
}
songs2.removeAll(itemsToRemove);
itemsToRemove.clear();
}
}
public void filterTitle(String s) {
int n = 0;
if (n == 0) {
System.out.println("Program is processing.");
n++;
for (Song song1 : songs2) {
if ((!(((song1.title).contains(s))))) {
itemsToRemove.add(song1);
}
}
songs2.removeAll(itemsToRemove);
itemsToRemove.clear();
}
}
public void sortTitle() {
Collections.sort(songs2, SongComparator.byTitle()); // now we have a sorted list
}
public void sortRank() {
Collections.sort(songs2, SongComparator.byRank()); // now we have a sorted list
}
public void sortArtist() {
Collections.sort(songs2, SongComparator.byArtist()); // now we have a sorted list
}
public void sortYear() {
Collections.sort(songs2, SongComparator.byYear()); // now we have a sorted list
}
public String toString() {
String result = "";
for (int i = 0; i < songs2.size(); i++) {
result += " " + songs2.get(i);
}
return result;
}
}
SongComparator Class:
import java.util.Comparator;
public class SongComparator implements Comparator<Song> {
public enum Order{
YEAR_SORT, RANK_SORT, ARTIST_SORT, TITLE_SORT
}
private Order sortingBy;
public SongComparator(Order sortingBy){
this.sortingBy = sortingBy;
}
public static SongComparator byTitle() {
return new SongComparator(SongComparator.Order.TITLE_SORT);
}
public static SongComparator byYear() {
return new SongComparator(SongComparator.Order.YEAR_SORT);
}
public static SongComparator byArtist() {
return new SongComparator(SongComparator.Order.ARTIST_SORT);
}
public static SongComparator byRank() {
return new SongComparator(SongComparator.Order.RANK_SORT);
}
#Override
public int compare(Song song1, Song song2) {
switch (sortingBy) {
case YEAR_SORT:
System.out.println("test");
return Integer.compare(song1.year, song2.year);
case RANK_SORT:
System.out.println("test");
return Integer.compare(song1.rank, song2.rank);
case ARTIST_SORT:
System.out.println("test");
return song1.artist.compareTo(song2.artist);
case TITLE_SORT:
System.out.println("test");
return song1.title.compareTo(song2.title);
}
throw new RuntimeException(
"Practically unreachable code, can't be thrown");
}
}
After you output the filtered collection, your program doesn't terminate because you are still in a while loop looking for the next user input line. This is basically what your program is doing:
while (input.hasNextLine()) {
// stuff happens here
System.out.println(collection.toString());
/*
* System.setOut(console); //changes output to print back to console Scanner outputFileScanner = new Scanner(outputFile); //inputs data from file while ((outputFileScanner.hasNextLine()))
* { //while the file still has data System.out.println(outputFileScanner.nextLine()); //print } outputFileScanner.close(); out.close();
*/
}

How to asynchronously modify an array of Strings

I thought this was an interesting programming problem so I posted it even though I think I have a solution idea that is good enough, see (*). If anyone has an elegant solution I would love to see it!
I am working with a method that calls external library that does http requests to a server. I need to have K strings as input to be effective i.e. each invocation of the external resource is a HTTP request and I need to buffer up some data for effectiveness. (As an example let K be 200 and occurs as a token in a text with say 1% probability so I would need to process 20,000 tokens before finding the 200 input arguments).
Effectively what this does is: externalHTTP(commaDelimitedString) -> get info about each string. Example externalHTTP("foo,bar") -> ["information snippet 1","information snippet 2"]. Where "information snippet 1" is about "foo".
I want to replace the "foo" and "bar" in a long text (string) with the information snippets but only after my buffer for the HTTP request is full. I still want to continue reading the original string while waiting for this to happen.
The text is tokenized by splitting (so I am working with an array of strings).
I.e. I would not like to stop execution of my text processing just because I am waiting for K strings to buffer up.
At first I thought that I could store words as individual string objects that I later update but then I realized that strings are immutable so it is call by value.
(*) My second idea was to store indices of the words (foo and bar) and then in order insert the snippets back into the original string array when the http request is finished. Like
class doStuff {
String text[];
LinkedList<Integer> idxList = new LinkedList<Integer>();
public doStuff(String[] t) {
text = t;
int i = 0;
for (String token : text) {
if (shouldReplaceToken(token)) {
replaceToken(i);
}
i++;
//do other work with the tokens
}
}
void replaceToken(int i) {
idxList.add(i);
if (++count > buffSize) {
count = 0;
String commaDelim = "";
ListIterator<Integer> it = idxList.getListIterator(0);
while (it.hasNext()) {
commaDelim += text[it.next()]+",";
}
String[] http_response = http_req(commaDelim);
for (String snippet : http_response) {
idx = idxList.poll(); //this is not elegant, dependent on FIFO order
text[Idx] = snippet;
}
}
}
}
To complicate things further is that I want to process several longer texts so i would have need to have a matrix of String arrays, one for each text.
I don't like the class known reference
String[] text
or they way I deal with indices in this code...
Hoping to see some suggestions :)
Edit: changed a bit to be more clear. I cant really say what I am looking up, non-disclosure etc, sorry. Some names might be different from java (bit only small difference).
Ok... Here's an attempt to fully answer your question with example code.
I've never played with threads much, so I figured I'd try to learn something tonight.
This solution uses threads to allow the http request to take place asynchronously.
The asynchronous request is simulated by using Thread.sleep().
My test case is primitive: the main class just sleeps for 30 sec to wait for everything to finish up.
Like I said, I'm new to Thread programming, so I probably overlooked something.
Hopefully this gets you started in the right direction...
/**
* A class that asynchronously replaces text in an
* array of strings by using helper threads.
*/
public class TextReplacer {
private final int bufferSize;
List<String> wordList = new ArrayList<String>();
List<Integer> indexList = new ArrayList<Integer>();
int bufferPosition = 0;
int lastPosition = 0;
public TextReplacer(String[] a, int n) {
bufferSize = n;
if (a != null) {
wordList = Arrays.asList(a);
}
}
public void replaceText() {
int i = 0;
for (String thisWord : getWordListCopy()) {
if (shouldReplaceToken(thisWord)) {
indexList.add(i);
processTextReplacement();
}
i++;
}
}
private void processTextReplacement() {
if (isBufferReady()) {
int currentPos = lastPosition;
replaceStrings(getCsv(), currentPos);
}
}
/** Uses a thread to replace strings in wordList. */
private void replaceStrings(String csv, int pos) {
new ReplacerThread(wordList, indexList, csv, pos, bufferSize).start();
}
private String getCsv() {
StringBuilder csv = new StringBuilder();
for (int i = 0; i < bufferSize; i ++) {
int idx = indexList.get(lastPosition++);
csv.append(wordList.get(idx)).append(",");
}
return csv.toString();
}
private boolean isBufferReady() {
bufferPosition++;
return ( bufferPosition % bufferSize == 0 );
}
private List<String> getWordListCopy() {
List<String> listCopy = new ArrayList<String>();
listCopy.addAll(wordList);
return listCopy;
}
/**
* Simulates a 10% replacement rate by only
* returning true for input that ends with a 3.
*/
private boolean shouldReplaceToken(String s) {
return s.endsWith("3");
}
public List<String> getWordList() {
return wordList;
}
public String[] getWordArray() {
return wordList.toArray(new String[0]);
}
}
/**
* A thread that sleeps for up to 8 seconds, then
* replaces a bunch of words in the list that is
* passed to it in its constructor.
*/
public class ReplacerThread extends Thread {
List<String> originalWords;
List<Integer> indices;
String wordCsv;
String[] replacementWords;
int startPos;
int bufferSize;
int maxSleepMillis = 8000;
int sleepMillis = getSleepMillis();
int threadNum; // for debugging
String prefix = new String(); // for debugging
/** Create a new thread. */
public ReplacerThread(List<String> o, List<Integer> i,
String c, int p, int n) {
originalWords = o;
indices = i;
wordCsv = c;
startPos = p;
bufferSize = n;
threadNum = startPos / bufferSize;
int count = 0;
while (count++ < threadNum) {
prefix += " ";
}
}
#Override
public void run() {
replacementWords = httpReq(wordCsv);
for (int i = 0; i < bufferSize; i ++) {
int pos = startPos + i;
int idx = indices.get(pos);
originalWords.set(idx, replacementWords[i]);
}
print("Thread #" + threadNum + " COMPLETE");
}
/** Simulate an asynchronous http request by using Thread.sleep */
private String[] httpReq(String s) {
try {
printSleepMessage();
sleep(sleepMillis);
}
catch (InterruptedException ex) {}
String[] repText = s.split(",");
for (int i = 0; i < repText.length; i++) {
repText[i] = repText[i].replace("Line", "Funky Line");
}
return repText;
}
private void printSleepMessage() {
int ms = sleepMillis / 1000;
print("Thread #" + threadNum + " SLEEP(" + ms + ")");
}
private int getSleepMillis() {
Double ms = maxSleepMillis * Math.random();
return ms.intValue();
}
public void print(Object o) {
String s = (o == null ? "null" : o.toString());
System.out.println(prefix + s + "\n");
}
}
/** A class that tests my funky solution. */
public class Main {
static String inputFile = "test-input.txt";
static int bufferSize = 50;
public static void main(String[] args) {
String[] theInput = readInput();
TextReplacer testItem = new TextReplacer(theInput, bufferSize);
testItem.replaceText();
try {
// wait 40 seconds for everything to happen
Thread.sleep(40000);
}
catch (InterruptedException ex) { }
dumpOutput(testItem.getWordArray());
}
public static String[] readInput() {
File inFile = new File(inputFile);
List<String> lineList = new ArrayList<String>();
try {
BufferedReader buff = new BufferedReader(new FileReader(inFile));
String currentLine = buff.readLine();
while (currentLine != null) {
lineList.add(currentLine);
currentLine = buff.readLine();
}
}
catch (IOException ignoreMe) {}
print("Lines read: " + lineList.size());
return lineList.toArray(new String[0]);
}
public static void dumpOutput(String[] txt) {
long ms = System.currentTimeMillis();
String fileName = "output-" + ms + ".txt";
File outFile = new File(fileName);
try {
BufferedWriter buff = new BufferedWriter(new FileWriter(outFile));
for (String s : txt) {
buff.write(s);
buff.newLine();
}
}
catch (IOException ignoreMe) {}
print("Lines written: " + txt.length);
print("File: " + fileName);
}
public static void print(Object o) {
System.out.println(o == null ? "null" : o.toString());
}
}

Is Levenshtein's distance the right way to tackle this Edit Steps problem?

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.

Categories