The following snippet of code is supposed to read from a text file, check how many words are in the file, and how many lines there are, and with that information it's supports to sort the nth word into it's appropriate array.
protected static void loadAccountInformationFromFile() throws Exception
{
// These variables control the logic needed to put words in the right array
int accountNumberCount = 1;
int firstNameCount = 2;
int lastNameCount = 3;
int balanceCount = 4;
int lastVariableCount = 5;
int sortCount = 1;
Scanner account = new Scanner(new File(INPUT_ACCOUNT_FILE)).useDelimiter(",");
// Using word count to get the nth value later
int wordCount = 0;
while(account.hasNext())
{
account.next();
wordCount++;
}
// If the count is zero it means the account list is empty
if (wordCount == 0)
{
System.err.println("error: Account list empty.");
System.exit(1);
}
// Used to test word count feature:
// System.out.println("Number of words: " + wordCount);
int lineCount = wordCount / MAX_VALUES_PER_LINE;
// These arrays will be used to put the account information in the correct place
// Using the lineCount to create the Array of however large it needs to be
String[] accountNumber = new String[lineCount - 1];
String[] firstName = new String[lineCount - 1];
String[] lastName = new String[lineCount - 1];
String[] balance = new String[lineCount - 1];
String[] lastVariable = new String[lineCount - 1];
// If I don't close and open a new Scanner I get a compiler error
account.close();
Scanner account2 = new Scanner(new File(INPUT_ACCOUNT_FILE)).useDelimiter(",");
do
{
String[] temp1 = account2.next().split(",");
String temp2 = "" + temp1;
if (sortCount == accountNumberCount)
{
accountNumber[sortCount] = temp2;
}
if (sortCount == firstNameCount)
{
firstName[sortCount] = temp2;
}
if (sortCount == lastNameCount)
{
lastName[sortCount] = temp2;
}
if (sortCount == balanceCount)
{
balance[sortCount] = temp2;
}
if (sortCount == lastVariableCount)
{
lastVariable[sortCount] = temp2;
}
accountNumberCount += 5;
firstNameCount += 5;
lastNameCount += 5;
balanceCount += 5;
lastVariableCount += 5;
sortCount++;
{
}
}
while (account2.hasNext());
// testing to see if it works, but it only returns:
// [null, [Ljava.lang.String;#55f96302, null, null, null]
System.out.println(Arrays.toString(accountNumber));
// This one only returns [Ljava.lang.String;#55f96302
System.out.println(accountNumber[1]);
account2.close();
}
It has no problem opening the file and counting the words properly. However, when it came time to put the words into the appropriate array it doesn't work, as indicated in the commenting out text.
My question is: Why? And how can I get this to work properly without using BufferedWriter / BufferedReader?
Based on an answer, I corrected a flaw in the logic only to end up with
run:
error: 6
Java Result: 1
BUILD SUCCESSFUL (total time: 0 seconds)
Here's the modified code:
public class Main
{
private final static int ACCOUNT_NUMBER_INDEX = 0;
private final static int FIRST_INDEX = 1;
private final static int LAST_INDEX = 2;
private final static int BALANCE_INDEX = 3;
private final static int FREE_CHECKS_INDEX = 4;
private final static int INTEREST_INDEX = 4;
private final static int MAX_VALUES_PER_LINE = 5;
private final static String INPUT_ACCOUNT_FILE = "accountInfo.txt";
protected static String[] fields;
protected static void loadAccountInformationFromFile() throws Exception
{
// These variables control the logic needed to put words in the right array
int accountNumberCount = 0;
int firstNameCount = 1;
int lastNameCount = 2;
int balanceCount = 3;
int lastVariableCount = 4;
int sortCount = 1;
Scanner account = new Scanner(new File(INPUT_ACCOUNT_FILE)).useDelimiter(",");
// Using word count to get the nth value later
int wordCount = 0;
while(account.hasNext())
{
account.next();
wordCount++;
}
// If the count is zero it means the account list is empty
if (wordCount == 0)
{
System.err.println("error: Account list empty.");
System.exit(1);
}
// Used to test word count feature:
// System.out.println("Number of words: " + wordCount);
int lineCount = wordCount / MAX_VALUES_PER_LINE;
// These arrays will be used to put the account information in the correct place
// Using the lineCount to create the Array of however large it needs to be
String[] accountNumber = new String[lineCount];
String[] firstName = new String[lineCount];
String[] lastName = new String[lineCount];
String[] balance = new String[lineCount];
String[] lastVariable = new String[lineCount];
// If I don't close and open a new Scanner I get a compiler error
account.close();
Scanner account2 = new Scanner(new File(INPUT_ACCOUNT_FILE)).useDelimiter(",");
do
{
String[] temp1 = account2.next().split(",");
String temp2 = "" + temp1;
if (sortCount == accountNumberCount)
{
accountNumber[sortCount] = temp2;
accountNumberCount += MAX_VALUES_PER_LINE;
}
if (sortCount == firstNameCount)
{
firstName[sortCount] = temp2;
firstNameCount += MAX_VALUES_PER_LINE;
}
if (sortCount == lastNameCount)
{
lastName[sortCount] = temp2;
lastNameCount += MAX_VALUES_PER_LINE;
}
if (sortCount == balanceCount)
{
balance[sortCount] = temp2;
balanceCount += MAX_VALUES_PER_LINE;
}
if (sortCount == lastVariableCount)
{
lastVariable[sortCount] = temp2;
lastVariableCount += MAX_VALUES_PER_LINE;
}
sortCount++;
}
while (account2.hasNext());
// testing to see if it works, but it only returns:
// [null, [Ljava.lang.String;#55f96302, null, null, null]
System.out.println(Arrays.toString(accountNumber));
// This one only returns [Ljava.lang.String;#55f96302
System.out.println(accountNumber[1]);
account2.close();
This happens because on every iteration of the loop, you increase all the count variables.
When coming to the loop for the first time you have the following variables:
accountNumberCount == 1
firstNameCount == 2
lastNameCount == 3
balanceCount == 4
lastVariableCount == 5
sortCount == 1
So sortCount == accountNumberCount and that means you put the word into accountNumber array to the second position (index starts from 0 on arrays).
Then you increase all the above variables and go for round two, having the following situation:
accountNumberCount == 6
firstNameCount == 7
lastNameCount == 8
balanceCount == 9
lastVariableCount == 10
sortCount == 2
Thus on second iteration sortCount no longer equals any of the other variables. Same thing happens on further iterations and you end up with only one word inserted into one of the five arrays.
If you want to make this work, I'd suggest using ArrayLists (so you don't have to worry about array indexes or array sizes) instead of arrays and to only change one variable per each iteration.
private static final int ACCOUNT_NUMBER_COUNT = 1;
private static final int FIRST_NAME_COUNT = 2;
private static final int LAST_NAME_COUNT = 3;
private static final int BALANCE_COUNT = 4;
private static final int LAST_VARIABLE_COUNT = 5;
List<String> accountNumbers = new ArrayList<String>();
List<String> firstNames = new ArrayList<String>();
List<String> lastNames = new ArrayList<String>();
List<String> balances = new ArrayList<String>();
List<String> lastVariables = new ArrayList<String>();
int sortCount = 1;
// Other things...
do {
if (sortCount == ACCOUNT_NUMBER_COUNT) {
accountNumbers.add(temp2);
} else if (sortCount == FIRST_NAME_COUNT) {
firstNames.add(temp2);
} else if (sortCount == LAST_NAME_COUNT) {
lastNames.add(temp2);
} else if (sortCount == BALANCE_COUNT) {
balances.add(temp2);
} else if (sortCount == LAST_VARIABLE_COUNT) {
lastVariables.add(temp2);
}
if (sortCount < 5) {
sortCount++;
} else {
sortCount = 1;
}
} while (account2.hasNext());
Personally I'd read line by line using BufferedReader, that way you do not need to calculate the number of lines,
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String line;
while ((line = br.readLine()) != null) {
// process the line.
}
}
Related
I am making a multiple string input random swap without using a temp variable.
But when I input, this happens a few times:
This happens more frequently... (note that the first output is always null and some outputs occasionally repeat)
My code:
import java.util.Arrays;
import java.util.Scanner;
public class myFile {
public static boolean contains(int[] array, int key) {
Arrays.sort(array);
return Arrays.binarySearch(array, key) >= 0;
}
public static void println(Object line) {
System.out.println(line);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String finalText = "";
String[] input = new String[5];
String[] swappedInput = new String[input.length];
int[] usedIndex = new int[input.length];
int swapCounter = input.length, useCounter;
for (int inputCounter = 0; inputCounter < input.length; inputCounter++) { //input
println("Enter input 1 " + (inputCounter + 1) + ": ");
input[inputCounter] = in.nextLine();
}
while (--swapCounter > 0) {
do{
useCounter = (int) Math.floor(Math.random() * input.length);
}
while (contains(usedIndex, useCounter));
swappedInput[swapCounter] = input[swapCounter].concat("#" + input[useCounter]);
swappedInput[useCounter] = swappedInput[swapCounter].split("#")[0];
swappedInput[swapCounter] = swappedInput[swapCounter].split("#")[1];
usedIndex[useCounter] = useCounter;
}
for (int outputCounter = 0; outputCounter < input.length; outputCounter++) {
finalText = finalText + swappedInput[outputCounter] + " ";
}
println("The swapped inputs are: " + finalText + ".");
}
}
Because of randomality some times useCounter is the same as swapCounter and now look at those lines (assume useCounter and swapCounter are the same)
swappedInput[swapCounter] = input[swapCounter].concat("#" + input[useCounter]);
swappedInput[useCounter] = swappedInput[swapCounter].split("#")[0];
swappedInput[swapCounter] = swappedInput[swapCounter].split("#")[1];
In the second line you are changing the value of xxx#www to be www so in the third line when doing split you dont get an array with two values you get an empty result thats why exception is thrown in addition you should not use swappedInput because it beats the pourpuse (if i understand correctly yoush shoud not use temp values while you are using addition array which is worse) the correct sollution is to only use input array here is the solution
public class myFile {
public static boolean contains(int[] array, int key) {
Arrays.sort(array);
return Arrays.binarySearch(array, key) >= 0;
}
public static void println(Object line) {
System.out.println(line);
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String finalText = "";
String[] input = new String[5];
int[] usedIndex = new int[input.length];
int swapCounter = input.length, useCounter;
for (int inputCounter = 0; inputCounter < input.length; inputCounter++) { //input
println("Enter input 1 " + (inputCounter + 1) + ": ");
input[inputCounter] = in.nextLine();
}
while (--swapCounter >= 0) {
do {
useCounter = (int) Math.floor(Math.random() * input.length);
}
while (contains(usedIndex, useCounter));
// Skip if results are the same
if (useCounter == swapCounter) {
swapCounter++;
continue;
}
input[swapCounter] = input[swapCounter].concat("#" + input[useCounter]);
input[useCounter] = input[swapCounter].split("#")[0];
input[swapCounter] = input[swapCounter].split("#")[1];
usedIndex[useCounter] = useCounter;
}
for (int outputCounter = 0; outputCounter < input.length; outputCounter++) {
finalText = finalText + input[outputCounter] + " ";
}
println("The swapped inputs are: " + finalText + ".");
}
}
After hard searchig I still haven't found the proper answer for my question and there is it:
I have to write a java program that enters an array of strings and finds in it the largest sequence of equal elements. If several sequences have the same longest length, the program should print the leftmost of them. The input strings are given as a single line, separated by a space.
For example:
if the input is: "hi yes yes yes bye",
the output should be: "yes yes yes".
And there is my source code:
public static void main(String[] args) {
System.out.println("Please enter a sequence of strings separated by spaces:");
Scanner inputStringScanner = new Scanner(System.in);
String[] strings = inputStringScanner.nextLine().split(" ");
System.out.println(String.join(" ", strings));
ArrayList<ArrayList<String>> stringsSequencesCollection = new ArrayList<ArrayList<String>>();
ArrayList<String> stringsSequences = new ArrayList<String>();
stringsSequences.add(strings[0]);
for (int i = 1; i < strings.length; i++) {
if(strings[i].equals(strings[i - 1])) {
stringsSequences.add(strings[i]);
} else {
System.out.println(stringsSequences + " " + stringsSequences.size());
stringsSequencesCollection.add(stringsSequences);
stringsSequences.clear();
stringsSequences.add(strings[i]);
//ystem.out.println("\n" + stringsSequences);
}
if(i == strings.length - 1) {
stringsSequencesCollection.add(stringsSequences);
stringsSequences.clear();
System.out.println(stringsSequences + " " + stringsSequences.size());
}
}
System.out.println(stringsSequencesCollection.size());
System.out.println(stringsSequencesCollection.get(2).size());
System.out.println();
int maximalStringSequence = Integer.MIN_VALUE;
int index = 0;
ArrayList<String> currentStringSequence = new ArrayList<String>();
for (int i = 0; i < stringsSequencesCollection.size(); i++) {
currentStringSequence = stringsSequencesCollection.get(i);
System.out.println(stringsSequencesCollection.get(i).size());
if (stringsSequencesCollection.get(i).size() > maximalStringSequence) {
maximalStringSequence = stringsSequencesCollection.get(i).size();
index = i;
//System.out.println("\n" + index);
}
}
System.out.println(String.join(" ", stringsSequencesCollection.get(index)));
I think it should be work correct but there is a problem - the sub array list's count isn't correct: All the sub arrayList's size is 1 and for this reason the output is not correct. I don't understand what is the reason for this. If anybody can help me to fix the code I will be gratefull!
I think it is fairly straight forward just keep track of a max sequence length as you go through the array building sequences.
String input = "hi yes yes yes bye";
String sa[] = input.split(" ");
int maxseqlen = 1;
String last_sample = sa[0];
String longest_seq = last_sample;
int seqlen = 1;
String seq = last_sample;
for (int i = 1; i < sa.length; i++) {
String sample = sa[i];
if (sample.equals(last_sample)) {
seqlen++;
seq += " " + sample;
if (seqlen > maxseqlen) {
longest_seq = seq;
maxseqlen = seqlen;
}
} else {
seqlen = 1;
seq = sample;
}
last_sample = sample;
}
System.out.println("longest_seq = " + longest_seq);
Lots of issues.
First of all, when dealing with the last string of the list you are not actually printing it before clearing it. Should be:
if(i == strings.length - 1)
//...
System.out.println(stringsSequences + " " + stringsSequences.size());
stringsSequences.clear();
This is the error in the output.
Secondly, and most importantly, when you do stringsSequencesCollection.add you are adding an OBJECT, i.e. a reference to the collection. When after you do stringsSequences.clear(), you empty the collection you just added too (this is because it's not making a copy, but keeping a reference!). You can verify this by printing stringsSequencesCollection after the first loop finishes: it will contain 3 empty lists.
So how do we do this? First of all, we need a more appropriate data structure. We are going to use a Map that, for each string, contains the length of its longest sequence. Since we want to manage ties too, we'll also have another map that for each string stores the leftmost ending position of the longest sequence:
Map<String, Integer> lengths= new HashMap<>();
Map<String, Integer> indexes= new HashMap<>();
String[] split = input.split(" ");
lengths.put(split[0], 1);
indexes.put(split[0], 0);
int currentLength = 1;
int maxLength = 1;
for (int i = 1; i<split.length; i++) {
String s = split[i];
if (s.equals(split[i-1])) {
currentLength++;
}
else {
currentLength = 1;
}
int oldLength = lengths.getOrDefault(s, 0);
if (currentLength > oldLength) {
lengths.put(s, currentLength);
indexes.put(s, i);
}
maxLength = Math.max(maxLength, currentLength);
}
//At this point, youll have in lengths a map from string -> maxSeqLengt, and in indexes a map from string -> indexes for the leftmost ending index of the longest sequence. Now we need to reason on those!
Now we can just scan for the strings with the longest sequences:
//Find all strings with equal maximal length sequences
Set<String> longestStrings = new HashSet<>();
for (Map.Entry<String, Integer> e: lengths.entrySet()) {
if (e.value == maxLength) {
longestStrings.add(e.key);
}
}
//Of those, search the one with minimal index
int minIndex = input.length();
String bestString = null;
for (String s: longestStrings) {
int index = indexes.get(s);
if (index < minIndex) {
bestString = s;
}
}
System.out.println(bestString);
Below code results in output as you expected:
public static void main(String[] args) {
System.out.println("Please enter a sequence of strings separated by spaces:");
Scanner inputStringScanner = new Scanner(System.in);
String[] strings = inputStringScanner.nextLine().split(" ");
System.out.println(String.join(" ", strings));
List <ArrayList<String>> stringsSequencesCollection = new ArrayList<ArrayList<String>>();
List <String> stringsSequences = new ArrayList<String>();
//stringsSequences.add(strings[0]);
boolean flag = false;
for (int i = 1; i < strings.length; i++) {
if(strings[i].equals(strings[i - 1])) {
if(flag == false){
stringsSequences.add(strings[i]);
flag= true;
}
stringsSequences.add(strings[i]);
}
}
int maximalStringSequence = Integer.MIN_VALUE;
int index = 0;
List <String> currentStringSequence = new ArrayList<String>();
for (int i = 0; i < stringsSequencesCollection.size(); i++) {
currentStringSequence = stringsSequencesCollection.get(i);
System.out.println(stringsSequencesCollection.get(i).size());
if (stringsSequencesCollection.get(i).size() > maximalStringSequence) {
maximalStringSequence = stringsSequencesCollection.get(i).size();
index = i;
//System.out.println("\n" + index);
}
}
System.out.println(stringsSequences.toString());
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 8 years ago.
Below is the error:
Exception in thread "main" java.lang.NullPointerException
at CellularData.addCountry(CellularData.java:34)
at CellularData.addCountry(CellularData.java:24)
at TestCSVReader.main(TestCSVReader.java:35)
I keep getting the error above but seem to have trouble fixing it. I am reading from a csv file and displaying the data on the screen. Basically it reads country, the year, and it stats of a cellular data in double. Below is CellularData class:
public class CellularData {
private Object [][]array;
private int year;
public CellularData(int rows, int columns, int year)
{
array = new Object[rows+1][columns+1]; //add +1 because initializes the header.
this.year = year;
for(int i=1;i<=columns;i++)
{
array[0][i] = year++; //increments the year
}
}
public void addCountry(String country, double []num)
{
for(int i=0;i<array.length;i++)
{
if(array[i][0] == null) //checks if the first row is empty
{
//CellularData.addCountry(CellularData.java:24)
addCountry(country, num, i); //inserts the data
break;
}
}
}
private void addCountry(String country, double []num, int row)
{
array[row][0] = country;
for(int j = 1;j<array[row].length;j++)
{
array[row][j] = num[j-1]; //CellularData.addCountry(CellularData.java:34)
}
}
public double getNumSubscriptionsInCountryForPeriod(String country, int sYear, int eYear)
{
double sum = 0;
for (int i = 1; i < array.length; i++) {
if (country.equalsIgnoreCase((String) array[i][0])) { //matches with the parameters passed ignoring CAPS
int start = 1 + sYear - year; //first index position of the year
int end = start + (eYear - sYear); //end position of the year
if (start >= 0 && end < array[i].length) { //checks to see if index position is out of bounds
for (int k = start; k <= end; k++) {
// System.out.println(">> " + country + " adding " + array[i][k]);
sum += (Double) array[i][k]; //sum of the stats
}
}
else {
//returns Error messgae and -1
System.out.println("ERROR : requested year "+sYear+" from "+ country+" is less than starting year "+this.year);
sum = -1;
}
}
}
return sum;
}
public String toString()
{ //prints the array.
for(Object []a: array)
{
for(Object k:a)
{
System.out.print(k + "\t");
}
System.out.println();
}
return " ";
}
}
Here is my csv reader file and this where i read from the file:
public class CSVReader {
String[] countryNames;
int[] yearLabels;
double[][] cellularDatatables;
Scanner scan;
public CSVReader(String filename)// throws FileNotFoundException
{
try{
File file = new File(filename);
scan = new Scanner(file);
scan.nextLine();
String numLine = scan.nextLine();
final int n = Integer.parseInt(numLine.split(",")[1]); //Number is the string portion after the first comma
//Allocate arrays with length n
countryNames = new String[n];
cellularDatatables = new double[n][];
//Read in the header line of years, parse and copy into yearNum
String[] yearHeaders = scan.nextLine().split(",");
final int m = yearHeaders.length-1;
yearLabels = new int[m];
for(int i=0;i<m;i++)
{
yearLabels[i] = Integer.parseInt(yearHeaders[i+1]); //i+1 to skip the first entry in the string arr
}
//Now read until we run out of lines - put the first in country names and the rest in the table
int c = 0;
while(scan.hasNext())
{
String[] inputArr = scan.nextLine().split(",");
countryNames[c] = inputArr[0];
cellularDatatables[c] = new double[m];
for(int i = 0; i < m; i++)
{
cellularDatatables[c][i] = Double.parseDouble(inputArr[i+1]);
}
}
scan.close();
}
catch(FileNotFoundException e)
{
System.out.println(e.getMessage());
}
}
public String[] getCountryNames(){
return countryNames;
}
public int[] getYearLabels(){
return yearLabels;
}
public double[][] getParsedTable(){
return cellularDatatables;
}
public int getNumberOfYears()
{
return yearLabels.length;
}
}
And also TestCSVReader file:
public static void main(String[] args)
{
final String FILENAME = "data/cellular.csv";
CSVReader parser = new CSVReader(FILENAME);
String [] countryNames = parser.getCountryNames();
//System.out.println(countryNames.length);
int [] yearLabels = parser.getYearLabels();
//System.out.print(yearLabels.length);
double [][] parsedTable = parser.getParsedTable();
CellularData datatable;
int numRows = parsedTable.length;
int numColumns =parser.getNumberOfYears();
int startingYear = yearLabels[0];
datatable = new CellularData(numRows, numColumns, startingYear);
for (int countryIndex = 0; countryIndex < countryNames.length; countryIndex++)
{
double [] countryData = parsedTable[countryIndex];
datatable.addCountry(countryNames[countryIndex], countryData);//Error TestCSVReader.java:35
}
System.out.printf(countryNames[0] + " (1960 to 2012): %.2f \n", datatable.getNumSubscriptionsInCountryForPeriod(countryNames[0],1960,2012));
// the output is: Aruba (1960 to 2012): 1170.50
array[row][0] = country;
for(int j = 1;j<array[row].length;j++)
{
array[row][j] = num[j-1]; //CellularData.addCountry(CellularData.java:34)
}
Since the first line doesn't give NullPointerException, we know that array is not null and array[row] is not null. Therefore, num must be null.
So I'm running a program that does various things to a String array. One of them is a inserting a string inside the array and sorting it. I'm able to use the sort method, but when I attempt to insert a string and then sort it I get a NullPointerException. This is the code:
import java.util.Scanner;
import java.io.*;
public class List_Driver
{
public static void main(String args[])
{
Scanner keyboard = new Scanner(System.in);
int choice = 1;
int checker = 0;
String [] words = new String[5];
words[0] = "telephone";
words[1] = "shark";
words[2] = "bob";
ListWB first = new ListWB(words);
int menu = uWB.getI("1. Linear Seach\n2. Binary Search\n3. Insertion in Order\n4. Swap\n5. Change\n6. Add\n7. Delete\n8. Insertion Sort\n9. Quit\n");
switch(menu)
{
//other cases
case 3:
{
String insert = uWB.getS("What term are you inserting?");
first.insertionInOrder(insert);
first.display();
}//not working
break;
}//switch menu
}//main
}//List_Driver
uWB is a basic util driver. It doesn't have any problems. This is the ListWB file itself:
public class ListWB
{
public void insertionSort()
{
for(int i = 1; i < size; i++)
{
String temp = list[i];
int j = i;
while(j > 0 && temp.compareTo(list[j-1])<0)
{
list[j] = list[j-1];
j = j-1;
}
list[j] = temp;
}
}
public void insertionInOrder(String str)
{
insertionSort();
int index = 0;
if(size + 1 <= list.length)
{
while(index < size && str.compareTo(list[index])>0)
index++;
size++;
for (int x = size -1; x> index; x--)
list[x] = list[x-1];
list[index] = str;
}
else
System.out.println("Capacity Reached");
}//insertioninorder
}//ListWB
How would I fix this?
You have an array of 5 Strings, but only 3 of them initialized. The rest points to null (because you did not initialize them):
String [] words = new String[5];
words[0] = "telephone";
words[1] = "shark";
words[2] = "bob";
words[3] = null;
words[4] = null;
The first line only initializes the array itself, but not the containing objects.
But the insert iterates over all 5 elements. And temp is null, when i is 3. So the statement temp.compareTo throws an NullPointerException.
for(int i = 1; i < size; i++)
{
String temp = list[i];
int j = i;
while(j > 0 && temp.compareTo(list[j-1])<0)
Solution: Also check temp for null in the while loop. Or do not use a string array at all but a auto-resizable data structure list java.util.ArrayList.
I am trying to solve a HackerRank challenge, but I am running into a problem. Obviously the brute force solution of O(n^2) will not cut if for the performance requirements (I tried), so I began searching for a more elegant solution. That is when I landed on KMP. And following this tutorial I implemented it.
However, the challenge states that you can actually have one mismatch in the strings, so I tried to add that functionality in my code. However, I am getting results that are not correct and I am completely clueless as to why. Could someone please take a look at my solution and point me in the right direction?
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
//This failure function creates an array of integers
//that indicate if there is a prefix that is both a
//prefix and a suffix of the word and at what position
//the prefix ends
private static int[] failureFunction(char[] p){
int i = 0;
int j = -1;
int len = p.length;
int[] f = new int[len+1];
f[i] = j;
int potentialWrong = 0;
while(i<len){
// if(j>=0 && p[i] != p[j]){
// potentialWrong++;
// }
// if(potentialWrong > 1){
// potentialWrong = 0;
while(j>=0 && p[i] != p[j]){
//if there is a mismatch consider the
//next widest border. The borders to be
//examined are obtained in decreasing order
// from the values f[i], f[f[i]] etc.
j=f[j];
}
// }
i++;
j++;
f[i]=j;
}
// for(int k : f){
// System.out.print(k+" ");
// }
return f;
}
private static LinkedList<Integer> searchKMP(char[] text, char[] ptrn){
int[] b = failureFunction(ptrn);
int j=0;
int i=0;
// pattern and text lengths
int ptrnLen = ptrn.length;
int txtLen = text.length;
int potentialWrong = 0;
LinkedList<Integer> list = new LinkedList<Integer>();
while(i<txtLen){
// System.out.println("i: "+i +", j: " +j);
if(text[i] != ptrn[j]){
potentialWrong++;
}
System.out.println("txt: " +text[i] +", ptrn: "+ptrn[j]);
System.out.println("Num wrong: " + potentialWrong);
if(potentialWrong > 1){
potentialWrong = 0;
while (j >= 0 && text[i] != ptrn[j]) {
j = b[j];
}
}
i++;
j++;
// a match is found
if (j == ptrnLen) {
list.add(i - ptrnLen);
j = b[j];
}
}
return list;
}
// private static boolean isValidMatch(String maybe, String virus){
// int numWrong = 0;
// // System.out.println(maybe +"vs."+ virus);
// for(int i=0;i<maybe.length();i++){
// if(maybe.charAt(i) != virus.charAt(i)){
// numWrong++;
// }
// if(numWrong > 1 ) return false;
// }
// return true;
// }
// private static LinkedList<Integer> getMatches(String patient, String virus){
// LinkedList<Integer> indices = new LinkedList<Integer>();
// for(int i=0;i<patient.length();i++){
// if(i+virus.length()-1 < patient.length()){
// if(isValidMatch(patient.substring(i, i+virus.length()), virus)){
// indices.add(i);
// }
// }
// else{
// break;
// }
// }
// return indices;
// }
public static void main(String[] args) {
Scanner scn = new Scanner(System.in);
int T = scn.nextInt();
String patient;
String virus;
for(int i =0;i<T;i++){
scn.nextLine(); //for empty line
patient = scn.nextLine();
virus = scn.nextLine();
LinkedList<Integer> list = searchKMP(patient.toCharArray(), virus.toCharArray());
for(int k : list){
System.out.print(k+" ");
}
System.out.println("");
}
}
}
public class KMPStringSearch {
/**
* Searches for all occurances of the word in the sentence. Runs in O(n+k)
* where n is the word length and k is the sentence length.
*
* #param word The word that is being searched
* #param sentence The collections of word over which the search happens.
* #return The list of starting indices of the matched word in the sentence.
* Empty list in case of no match.
*/
public List<Integer> searchString(final String word, final String sentence) {
final List<Integer> matchedIndices = new ArrayList<>();
final int sentenceLength = sentence.length();
final int wordLength = word.length();
int beginMatch = 0; // the starting position in sentence from which the match started
int idxWord = 0; // the index of the character of the word that is being compared to a character in string
final List<Integer> partialTable = createPartialMatchTable(word);
while (beginMatch + idxWord < sentenceLength) {
if (word.charAt(idxWord) == sentence.charAt(beginMatch + idxWord)) {
// the characters have matched
if (idxWord == wordLength - 1) {
// the word is complete. we have a match.
matchedIndices.add(beginMatch);
// restart the search
beginMatch = beginMatch + idxWord - partialTable.get(idxWord);
if (partialTable.get(idxWord) > -1) {
idxWord = partialTable.get(idxWord);
} else {
idxWord = 0;
}
} else {
idxWord++;
}
} else {
// mismatch. restart the search.
beginMatch = beginMatch + idxWord - partialTable.get(idxWord);
if (partialTable.get(idxWord) > -1) {
idxWord = partialTable.get(idxWord);
} else {
idxWord = 0;
}
}
}
return Collections.unmodifiableList(matchedIndices);
}
/**
* Creates the Partial Match Table for the word. Runs in O(n) where n is the
* length of the word.
*
* #param word The word whose Partial Match Table is required.
* #return The table as a list of integers.
*/
public List<Integer> createPartialMatchTable(final String word) {
if (StringUtils.isBlank(word)) {
return Collections.EMPTY_LIST;
}
final int length = word.length();
final List<Integer> partialTable = new ArrayList<>(length + 1);
partialTable.add(-1);
partialTable.add(0);
final char firstChar = word.charAt(0);
for (int idx = 1; idx < word.length(); idx++) {
final int prevVal = partialTable.get(idx);
if (prevVal == 0) {
if (word.charAt(idx) == firstChar) {
partialTable.add(1);
} else {
partialTable.add(0);
}
} else if (word.charAt(idx) == word.charAt(prevVal)) {
partialTable.add(prevVal + 1);
} else {
partialTable.add(0);
}
}
return Collections.unmodifiableList(partialTable);
}
}