How can I implement binary search to find a string with a particular prefix in generic array (which in this case will be a string[]). I tried compareTo but that wouldn't help because i have to use a string prefix. eg String prefix "Bi" bill, bilards ...etc..
Implement the following method to return all strings in an alphabetically sorted array that start with a given prefix. For instance, given a prefix “bi”, the returned strings are ”Bill Clinton”, ”Bill Gates”, and ”Bill Joy”. Note that all string comparisons should be case INSENSITIVE. The strings in the returned list must be in the order in which they appear in the array. Your implementation must be based on binary search, and must run in worst case O(log n+k) time, where n is the length of the array, and k is the number of matching strings. Assume that the array has no duplicate entries. If there are no matches, you may either return null, or an empty array list.
You may use the following String methods (in addition to any others you may recall):
boolean startsWith(String s)
int compareTo(String s)
int compareToIgnoreCase(String s)
String toLowerCase(String s)
String toUpperCase(String s)
(As for ArrayList, you only need to use the add method to add an item to the end of the array list.)
You may write helper methods (with full implementation) as necessary. You may not call any method that you have not implemented yourself
public static <T extends Comparable<T>> ArrayList prefixMatch(T[] list, String prefix) {
ArrayList<T> result = new ArrayList<T>();
int lo = 0;
int hi = list.length - 1;
while(lo <= hi) {
int mid = (hi + lo) / 2;
list[mid].startsWith(prefix) ? 0 : list[mid].compareTo((T) prefix));
}
return null;
}
You can use default binary search with custom comparator as your base, and then work our range by your self. I think the right algorithm would be:
Perform binary search on given array. Use comparator which checks only for prefix.
As result you'll get index of string which starts with your prefix
Walk to the left to find first string which matches prefix, remember position.
Walk to the right to find first string which matches prefix, remember position.
Copy elements from range start to range end from original array. That will be your desired array of all elements with prefix match condition.
Below is implementation in java. It works in happy case scenario but will crash if(I left those checks out to make code look simple):
No strings with given prefix exist in original array
There are string with length less then prefix length
Also if you need binary search implementation you could check source of Arrays.binarySearch
public class PrefixMatch {
public static void main(String[] args) {
final String[] prefixMathces = prefixMatch(new String[] { "Abc", "Abcd", "Qwerty", "Pre1", "Pre2", "Pre3", "Xyz", "Zzz" }, "pre");
for (int i = 0; i < prefixMathces.length; i++)
System.out.println(prefixMathces[i]);
}
public static String[] prefixMatch(final String[] array, final String prefix) {
final Comparator<String> PREFIX_COMPARATOR = new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return o1.substring(0, prefix.length()).compareToIgnoreCase(o2);
}
};
final int randomIndex = Arrays.binarySearch(array, prefix, PREFIX_COMPARATOR);
int rangeStarts = randomIndex, rangeEnds = randomIndex;
while (rangeStarts > -1 && array[rangeStarts].toLowerCase().startsWith(prefix.toLowerCase()))
rangeStarts--;
while (rangeEnds < array.length && array[rangeEnds].toLowerCase().startsWith(prefix.toLowerCase()))
rangeEnds++;
return Arrays.copyOfRange(array, rangeStarts + 1, rangeEnds);
}
}
I assume that you currently have something like this? :
arrayElement.compareTo(prefix)
If so, you can change it to look like this:
arrayElement.startsWith(prefix) ? 0 : arrayElement.compareTo(prefix)
I suggest looking into the API code for this. There is an Arrays class that you can check out in the java.lang package and learn from there.
Working on a similar problem right now. I believe pseudo code will go something like yours. I created a pojo class Song. A song is made up up three strings artist,title, and lyrics.
When you create a song object you get :
// Artist Title Lyrics..
Song a = ["Farmer Brown", "Oh' Mcdonalad", "Oh'mcdonal had a farm eh i oh i oh"]
public class Song implements Comparable<Song> {
private String _artist;
private String _lyrics;
private String _title;
// constructor
public Song(String artist, String title, String lyrics) {
this._artist = artist;
this._title = title;
this._lyrics = lyrics;
}
public String getArtist() {
return _artist;
}
public String getLyrics() {
return _lyrics;
}
public String getTitle() {
return _title;
}
public String toString() {
String s = _artist + ", \"" + _title + "\"";
return s;
}
//This compare two song objects
public int compareTo(Song song) {
String currentSong = song.toString();
int x = currentSong.compareToIgnoreCase(this.toString());
return x;
}
This is your method here that will take in the array of songs and your prefix and use the compare method to see if they match. If they match the compareTo method returns a 0. If you get a 0 then you know you have found your song so return the arrayOfSongs[index where song is found].
I have not coded up my search yet but I modified yours to match my code. I have not tested it yet. I don't think you even need a compareTo method but you can use it. Also for scaling the binary search should return a list of songs that might match as you might have multiple songs that start with "xyz" . Kind of when you start searching on google with prefix "do" you get a drop down of "dog, donut,double" which gives the user something to choose like a search engine.
public static ArrayList<Song> search (String[] arrayOfSongs , String enteredPrefix) {
ArrayList<Song> listOfMatches = new ArrayList<Song>;
int mid;
int lo = 0;
int hi = arrayOfSongs.length - 1;
while(lo <= hi)
{
mid = (hi + lo) / 2;
if(arrayOfSongs[mid].startsWith(enteredPrefix))
{
System.out.println("Found a match, adding to list");
listOfMatches.add(arrayOfSongs[mid]);
}
}
return listOfMatches;
}
Once you have a listOfMatches of possible suspects of the song you want you can use the compareTo method in some way.
Related
This question already has an answer here:
Java: Find the index of a String in a list
(1 answer)
Closed 3 years ago.
I have an ArrayList of type Player and I am trying to search for a specific string and I'm having a lot of trouble.
Currently I'm using a boolean to see if the player contains the string. Then I use a for loop to search the list.
But it keeps outputting the final line of the file; I'm pretty sure im not searching properly.
String playerName = sc.next();
boolean isFound = players.contains(playerName);
int index = 0;
for (int i = 0; i < players.size(); i++) {
if (isFound) {
break;
}
index = i;
}
System.out.println(players.get(index));
System.out.println(isFound);
My Player class is defined as follows
(not sure if this helps)
public class Player extends Person {
public String pos;
public int g;
public int ab;
public int r; // various statistics for the player
public Player(String name, String team, String pos) {
this.name = name;
this.team = team;
this.pos = pos;
}
And i also have a league class containing the lists
public class League extends loadData {
public static ArrayList<Player> players = new ArrayList<Player>();
public static ArrayList<Pitcher> pitchers = new ArrayList<Pitcher>();
}
If anyone can help me out that would be much appreciated!
If you just need the index of an element in the list, use List#indexOf(). There is no need to check if the list contains the element first because if it does not, the method returns -1.
Example:
List<String> players = new ArrayList<String>
players.addAll(Arrays.asList("Jim, "Bob, "Mary");
int indexOfBob = players.indexOf("Bob");
If you plan on iterating through the list and performing an operation on all the elements until you reach a specific one, you don't need to use two index integers. Example:
int i;
for(i = 0; i < players.size(); i++) {
// Perform an operation on the current player.
if(players.get(i).equals("Bob")) break;
}
what's happening with your code
isFound is set only before loop. That's totally ok for checking if the string is contained within the list.
If the list doesn't contain the string, then the loop will never break and iterating through all elements. So in this case your index-counter i will point to the last element which then gets printed after the loop.
clarify type of list elements VS type of searched
If your list stores objects of Player you couldn't simply compare them to searched object String. Maybe you can — on each element of the list — compare for player.toString().contains(playerName) or you can check for player.getName().equalsIgnoreCase(playerName).
The type of you list's elements and it's declaration matter and need to be known (to us) in order to solve this.
solving search and print finding
assuming the list contains elements of type string, i.e. ArrayList<String> players
To check if an element (here string playerName) is contained you can use either (Array)List's indexOf or contains method.
Note: the string could be 0, 1, or multiple times contained in the list. The approaches above only find first or no occurrence.
For printing you can either print the search-term playerName directly if found. Or you can use the returned first found index of method-call index to get the matching element and print that.
Note: The text printed should be the same in both options, although instance-ids/hashCodes could vary.
I used following method to add my data to ArrayList.
ArrayList<Word> wordList = new ArrayList<Word>();
Word word = new Word();
word.set_id(id);
word.setWord(word);
word.setDefinition(definition);
wordList.add(word);
After the add some data, I want find the position of the any id which I want find in ArrayList.
Already I have tried following method to get position by id. But it isn't work.
int position = wordList.indexOf(id);
and
int position = wordList.lastIndexOf(id);
Both codes always generated "position = -1" as a result. How can I do that?
Edited
This is the code of the Word.java class. How can I implement equal method?
public class Word {
private String _id, word, definition, favourite, usage;
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
public String getDefinition() {
return definition;
}
public void setDefinition(String definition) {
this.definition = definition;
}
public String getFavourite() {
return favourite;
}
public void setFavourite(String favourite) {
this.favourite = favourite;
}
public String getUsage() {
return usage;
}
public void setUsage(String usage) {
this.usage = usage;
}
}
indexOf is trying to compare Word objects. Your list doesn't contain ids as the elements, so you get -1.
You need to use a loop and search the list.
int id = 3;
int position = -1;
for (int i = 0; i < wordlist.size(); i++) {
if (wordlist.get(i).getId() == id) {
position = i;
// break; // uncomment to get the first instance
}
}
Note: this will search the whole list to find the last index of that id. So if there are duplicates and you only want the first one (or stop the loop as soon as you find what you want) add a break in the if statement.
Implement equals method in the "Word" object. Inside equals method you can apply equals only to id field.
Create a new Word object with that id and pass that object in indexOf. Don't pass the id in the indexOf. Pass the new of existing Word object with the required id.
Then indexOf will return the valid index of this word object.
For searching the object in a list. you need to override equals method in your Word class. otherwise you will get -1. because indexOf internally used equals method to search the element in list.
The class inside your list should implement hascode() and equals() in order to have indexOf() that works.
Suppose I have a string String s = "0123456789t:9876543210"
How can I get the index immediately following "t:" (i.e, index 12)?
Is there a built-in way, or am I stuck with s.substring(s.indexOf("t:")+2); ?
Specifically, I'm wanting a solution for any token-length. So my token may be t:, or it may as easily be test:. Regardless of the token itself, is there a way to dynamically get the index immediately following whatever token I search on?
This is the correct way of finding index.
int indexOf(String str)
From Javadocs,
Returns the index within this string of the first occurrence of the
specified substring. The integer returned is the smallest value k such
that:
this.startsWith(str, k)
To make it dynamic, pls have a structure of this sort.
public static void main(String[] args) {
String s = "0123456789t:9876543210";
System.out.println(getIndex("t:", s));
s = s.substring(s.indexOf("t:") + 2);
System.out.println(s);
}
private static int getIndex(String searchedText, String inputText) {
return inputText.indexOf(searchedText) + searchedText.length();
}
output
12
9876543210
You can use IndexOf like this:
s.indexOf(s + ":")+2
Why not just make it all modular having an arraylist of strings, with all commands and for it you can do:
ArrayList<String> al = new ArrayList<String>();
String s = "123456789t:987654321";
al.add("t:");//this is where you add your cases
for(String c:al){
System.out.println(s.substring(s.indexOf(c) + c.length));
}
that way the length of the case cant be messed up b/c it is part of the string class
Hi all I am very new for the Java. I would like to sort below array of strings as per LastName then on FirstName without use of any API i.e. I am not supposed to use Arrays.sort() , compareTo(), equals() etc..
Input array String
String [][]name={{"Jen","Eric"},
{"Brain","Adams"},
{"Jon","Methew"},
{"Antino","Ronald"},
{"Cris","Ronald"}
};
my out put should be like.
Brain,Adams
Jen,Eric
Jon,Methew
Antino,Ronald
Cris,Ronald
Please Help.
public class StringArraySort {
public static void main(String[] args) {
//System.out.println(str.length);
String [][]name={{"Jen","Eric"},
{"Brain","Adams"},
{"Jon","Methew"},
{"Antino","Ronald"},
{"Cris","Ronald"}
};
String []str1= new String [name.length];
String []str2= new String [name.length];
for(int i=1;i<name.length;i++)
{
int j=i;
str1[i]=name[i][j];
str2[i]=name[i-1][j];
//System.out.println(str1[i]+" "+str2[i]);
}
/*for(String tmp:name)
{
char a[] = new char[tmp.length()] ;
//System.out.println(tmp);
for(int i=0;i<tmp.length();i++)
{
a[i]=tmp.charAt(i);
System.out.println(a[i]);
}
}*/
}
}
I will not give you any code, as this is clearly an assignment, but here's some general guidance:
Don't try to put everything into main. You may not be allowed to use any exiting API, but you can define your own! Write your own compare and sort methods.
Start with a method compare(String, String) -> int, or isSmaller(String, String) -> boolean. Use String.toCharArray to get the individual characters and compare them, in pairs from both strings. Make sure to handle the case of the strings having different lengths.
Now write a method compare(String[], String[]) -> int. This can look very similar to the above (in fact, you could make a generic one for both), but it might be simpler to make this one specific for the "lastname-firstname" case, particularly since here you want to sort by the second element first.
Finally, write your own sort method. An in-place bubble sort should be the easiest and the algorithm can easily be found on the internet. Other sort algorithms are faster, but if speed is an issue, the requirement not to use any API is nonsensical in the first place. If you want to score bonus-points, though, you can try to implement an in-place quick sort, but only after you've got it running with the bubble sort.
Also, you should test each of those methods individually. Don't try to run your sort method before you've made sure your compare methods actually work. Call them individually with different outputs and see whether they yield the correct result.
public class NameSort {
public static void main(String[] args) {
String [][] names={{"Jen","Eric"},
{"Brain","Adams"},
{"Jon","Methew"},
{"Antino","Ronald"},
{"Cris","Ronald"}
};
for(int m=0;m<names.length;m++)
{
for(int n=m+1;n<names.length;n++)
{
if(myCompare(names[m][1],names[n][1])==1)
{
swap(names, names[m], names[n], m, n);
}
else if (myCompare(names[m][1],names[n][1])==0)
{
if(myCompare(names[m][0],names[n][0])==1)
{
swap(names, names[m], names[n], m, n);
}
}
}
}
for (int i=0;i<names.length;i++)
{
System.out.println(names[i][0]+" " +names[i][1] );
}
}
public static void swap(String [][] names,String[] a,String[] b,int m,int n)
{
names[n]=a;
names[m]=b;
}
public static int myCompare(String a, String b)
{
int minLength= a.length()<b.length()?a.length():b.length();
for(int i=0;i<minLength;i++)
{
if(a.charAt(i)>b.charAt(i))
{
return 1;
}
else if(a.charAt(i)<b.charAt(i)){
return -1;
}
}
if(a.length()>minLength)
return 1;
else if (b.length()> minLength )
return -1;
else
return 0;
}
}
In order to let you learn at least something, I am going to give you the answer in psuedo-code and let you do the coding. The solution is based on bubble sort and comparing names (=Strings) by looping on their characters
in bubble sort we iterate over the array, in each iteration, we compare two adjacent cells and possibly swap them so that they are in the correct order.
at the end of the 1st iteration, the biggest cell will be in the correct position (=last). so we start another iteration but skip the last cell. by the end of the 2nd iteration, the 2nd biggest cell will in its correct position. we cotinue iterating, each time going over one less cell until there are no more cells to iterate over.
I give you the comparing method:
The solution assumes you are allowed to call length() and charAt() methods of String class.
/**
* returns negative, zero or positive value
* if s1 is smaller, equal or bigger than s2, respectively
* comparison is lexicographical
*/
static int compareStrings(String s1, String s2)
{
int i = 0;
for (i = 0; i < s1.length() && i < s2.length(); i++) {
int diff = s1.charAt(i) - s2.charAt(i);
if (diff != 0) return diff;
}
if (i == s1.length()) {
if (i == s2.length()) return 0; // equal lengths
else return 1; // exhausted s2 before s1
}
return -1; // exhausted s1 before s2
}
seeing the loop in your code, I think one last note is in order: you should be aware that arrays in Java start with index 0 and the last cell is at length-1.
I have a file of the following format update_0_9_26. I want to write java code which will check for all files having a version greater than 0_9_26 . For example 0_9_27 and so on. I have to perform processing on the files which are retrieved from this calculation.
How should I parse the filename in order to retrieve those greater than the referece name?
Example input:
update_0_9_26.sql
update_0_9_27.sql
update_0_9_28.sql
update_1_0_0.sql
update_1_0_1.sql
update_1_0_2.sql
Expected output (when searching for "larger than update_0_9_26"):
update_0_9_27.sql
update_0_9_28.sql
Simple step-by-step guide:
Get the file-names from the filesystem, using new File("myDirName").list()
Sort that list into a TreeSet created using a custom comparator (see below).
Iterate through the set, processing your files in order, but not including the ones you have already seen. Use myTreeSet.tailSet(lastFileToNotProcess, 'true') to get a view of the set that contains only those elements.
The fun part is in step 2. Here is a custom comparator that tokenizes its input and compares integer tokens numerically, and non-integer tokens alphabetically:
class TokenizingComparator implements Comparator<String> {
private String separator;
private static Pattern intPattern = Pattern.compile("[0-9]+");
public TokenizingComparator(String separator) { this.separator = separator; }
public int compare(String a, String b) {
String pa[] = a.split(separator);
String pb[] = b.split(separator);
for (int ia=0, ib=0; ia<pa.length && ib<pb.length; ia++,ib++) {
int rc = comparePart(pa[ia], pb[ib]);
if (rc != 0) return rc;
}
return (pa.length != pb.length) ?
pa.length - pb.length : 0;
}
private comparePart(String a, String b) {
if (intPattern.matcher(a).matches() && intPattern.matcher(b).matches()) {
return Integer.parseInt(a) - Integer.parseInt(b);
} else {
return a.compareTo(b);
}
}
}