My code below is supposed to sort version numbers into proper order. For the most part it works, but it fails a hidden test case that I don't have access to. Given that is there any edge case you can see that I might be missing.
import java.util.*;
public class Answer {
public static void main(String[] args)
{
//Testing
String[] versions = {"0.0.0","0","0.0","1.113","0.0.0.1","2.0.0","1.2","2","0.1","1.2.1","1.1.1","2.0"};
String[] results = answer(versions);
for(int i =0; i<results.length;i++)
{
System.out.println(results[i]);
}
}
public static String[] answer(String[] l) {
String temp = new String();
//Insertion sort on the given array to assign correct version numbers
for (int i = 1; i < l.length; i++) {
for(int j = i ; j > 0 ; j--){
if(compareVersion(l[j],l[j-1])<0){
temp = l[j];
l[j] = l[j-1];
l[j-1] = temp;
}
}
}
return l;
}
//Will compare version numbers breaking it apart into a String array
public static int compareVersion(String version1, String version2) {
String[] arr1 = version1.split("\\.");
String[] arr2 = version2.split("\\.");
int i=0;
while(i<arr1.length || i<arr2.length){
if(i<arr1.length && i<arr2.length){
if(Integer.parseInt(arr1[i]) < Integer.parseInt(arr2[i])){
return -1;
}else if(Integer.parseInt(arr1[i]) > Integer.parseInt(arr2[i])){
return 1;
}
else if(Integer.parseInt(arr1[i]) == Integer.parseInt(arr2[i]))
{
int result = specialCompare(version1,version2);
if(result != 0)
{
return result;
}
}
} else if(i<arr1.length){
if(Integer.parseInt(arr1[i]) != 0){
return 1;
}
} else if(i<arr2.length){
if(Integer.parseInt(arr2[i]) != 0){
return -1;
}
}
i++;
}
return 0;
}
// Meant for when version numbers such as 2 and 2.0 arise. This method will make sure to
// put the smaller version number ( in length) first
public static int specialCompare(String str1, String str2)
{
String[] arr1 = str1.split("\\.");
String[] arr2 = str2.split("\\.");
for(int i =1; i<arr1.length;i++)
{
if(Integer.parseInt(arr1[i]) != 0)
{
return 0;
}
}
for(int j =1; j<arr2.length;j++)
{
if(Integer.parseInt(arr2[j]) != 0)
{
return 0;
}
}
if(arr1.length < arr2.length)
{
return -1;
}
else
{
return 1;
}
}
}
I read Lukas Eder's blog post in the comment above and created a java.util.Comparator which orders by version (or chapter) number, based on a JDK proposal.
VersionNumberComparator is defined in a GitHub gist. The follow code shows how it works.
import java.util.ArrayList;
import java.util.List;
public class JavaTest {
public static void main(String[] args) {
final List<String> chapters = new ArrayList<>();
chapters.add("1.1");
chapters.add("1.2");
chapters.add("1");
chapters.add("1.3");
chapters.add("1.1.1");
chapters.add("5.6");
chapters.add("1.1.10");
chapters.add("4");
chapters.add("1.1.9");
chapters.add("1.2.1.10");
chapters.add("2.1.1.4.5");
chapters.add("1.2.1.9");
chapters.add("1.2.1");
chapters.add("2.2.2");
chapters.add("1.2.1.11");
System.out.println("UNSORTED: " + chapters.toString());
chapters.sort(VersionNumberComparator.getInstance());
System.out.println("SORTED: " + chapters.toString());
}
}
Produces the following output:
UNSORTED: [1.1, 1.2, 1, 1.3, 1.1.1, 5.6, 1.1.10, 4, 1.1.9, 1.2.1.10, 2.1.1.4.5, 1.2.1.9, 1.2.1, 2.2.2, 1.2.1.11]
SORTED: [1, 1.1, 1.1.1, 1.1.9, 1.1.10, 1.2, 1.2.1, 1.2.1.9, 1.2.1.10, 1.2.1.11, 1.3, 2.1.1.4.5, 2.2.2, 4, 5.6]
I have recently had a need to do this in a more generic way for arbitrary file names, similar to how Windows Explorer sorts files:
I wrote a blog post about this. The idea was inspired by this answer here. The comparator used for ordering files this way looks like this:
public final class FilenameComparator implements Comparator<String> {
private static final Pattern NUMBERS =
Pattern.compile("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
#Override
public final int compare(String o1, String o2) {
// Optional "NULLS LAST" semantics:
if (o1 == null || o2 == null)
return o1 == null ? o2 == null ? 0 : -1 : 1;
// Splitting both input strings by the above patterns
String[] split1 = NUMBERS.split(o1);
String[] split2 = NUMBERS.split(o2);
for (int i = 0; i < Math.min(split1.length, split2.length); i++) {
char c1 = split1[i].charAt(0);
char c2 = split2[i].charAt(0);
int cmp = 0;
// If both segments start with a digit, sort them numerically using
// BigInteger to stay safe
if (c1 >= '0' && c1 <= '9' && c2 >= '0' && c2 <= '9')
cmp = new BigInteger(split1[i]).compareTo(new BigInteger(split2[i]));
// If we haven't sorted numerically before, or if numeric sorting yielded
// equality (e.g 007 and 7) then sort lexicographically
if (cmp == 0)
cmp = split1[i].compareTo(split2[i]);
// Abort once some prefix has unequal ordering
if (cmp != 0)
return cmp;
}
// If we reach this, then both strings have equally ordered prefixes, but
// maybe one string is longer than the other (i.e. has more segments)
return split1.length - split2.length;
}
}
If you want to sort some versions (e.g. 0.4.3, 5.3.5, 1.2.4) you could use my approach which includes using a java.util.Comparator. To use this you have to use a sorting method (e.g. Arrays.sort(new String[] {"0.4.3", "5.3.5", "1.2.4"}, new VersionComparator())). The VersionComparator class is written below:
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public final class VersionComparator implements Comparator<String> {
#Override
public int compare(String version1, String version2) {
if (checkVersionFormat(version1) == false || checkVersionFormat(version2) == false) {
System.out.println("One of the given versions uses a false format");
}
List<String> firstVersionElements = Arrays.asList(version1.split("\\."));
List<String> secondVersionElements = Arrays.asList(version2.split("\\."));
int maxVersionElements = getMaxNumber(firstVersionElements.size(), secondVersionElements.size(), 0);
for (int counter = 0; counter < maxVersionElements; counter++) {
if (firstVersionElements.size() == counter && secondVersionElements.size() == counter) {
return 0;
}
if (firstVersionElements.size() == counter) {
return 1;
}
if (secondVersionElements.size() == counter) {
return -1;
}
int firstIntElement = Integer.valueOf(firstVersionElements.get(counter));
int secondIntElement = Integer.valueOf(secondVersionElements.get(counter));
if (firstIntElement < secondIntElement) {
return 1;
}
if (firstIntElement > secondIntElement) {
return -1;
}
}
return 0;
}
private boolean checkVersionFormat(String version) {
String versionCopy = new String(version);
String[] validChars = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "." };
for (String character : validChars) {
versionCopy = versionCopy.replace(character, "");
}
return versionCopy.equals("");
}
public static int getMaxNumber(Integer... ints) {
int maximumInt = ints[0];
for (int processInt : ints) {
if (processInt > maximumInt) {
maximumInt = processInt;
}
}
return maximumInt;
}
}
From your comments in your code above the specialCompare method…
Meant for when version numbers such as 2 and 2.0 arise. This method
will make sure to put the smaller version number (in length) first
So I am guessing from this that you want the version to be sorted by the length of the version number. Example: from your supplied versions string array you want to sort as below…
0
2
0.0
0.1
1.2
1.113
2.0
0.0.0
1.1.1
1.2.1
2.0.0
0.0.0.1
If this is the case, then you seem to be making this more complicated than it has to be. When you get two versions where the version lengths differ, then the one with the shorter version length should go first. So a simple check on the length of the version split arrays should solve this. If they are the same length, then you need to check each version. There is no need for a specialCompare method when the version lengths are the same. Simply check each version and if they are the same, then go to the next version number and so on. As soon as one version is different then you will know what to return. If you go through the whole array then you know all the version numbers are the same.
Below is a change to the compareVersion method using the logic above. There is no need for a specialCompare method. I am guessing this is what you are looking for.
public static int compareVersion(String version1, String version2)
{
String[] arr1 = version1.split("\\.");
String[] arr2 = version2.split("\\.");
if (arr1.length < arr2.length)
return -1;
if (arr1.length > arr2.length)
return 1;
// same number of version "." dots
for (int i = 0; i < arr1.length; i++)
{
if(Integer.parseInt(arr1[i]) < Integer.parseInt(arr2[i]))
return -1;
if(Integer.parseInt(arr1[i]) > Integer.parseInt(arr2[i]))
return 1;
}
// went through all version numbers and they are all the same
return 0;
}
package com.e;
import java.util.*;
/**
* Created by dpc on 17-2-27.
*
*/
public class VersionComparator implements Comparator {
#Override
public int compare(String o1, String o2) {
if (o1 == null && o2 == null) {
return 0;
} else if (o1 == null && o2 != null) {
return -1;
} else if (o1 != null && o2 == null) {
return 1;
} else {
if (o1.length() == 0 && o2.length() == 0) {
return 0;
} else if (o1.length() == 0 && o2.length() > 0) {
return -1;
} else if (o1.length() > 0 && o2.length() == 0) {
return 1;
} else {
return compareVersion(o1, o2);
}
}
}
public static int compareVersion(String version1, String version2) {
String[] arr1 = version1.split("\\.");
String[] arr2 = version2.split("\\.");
try {
int i = 0;
while (i < arr1.length || i < arr2.length) {
if (i < arr1.length && i < arr2.length) {
if (Integer.parseInt(arr1[i]) < Integer.parseInt(arr2[i])) {
return -1;
} else if (Integer.parseInt(arr1[i]) > Integer.parseInt(arr2[i])) {
return 1;
} else if (Integer.parseInt(arr1[i]) == Integer.parseInt(arr2[i])) {
int result = specialCompare(version1, version2);
if (result != 0) {
return result;
}
}
} else if (i < arr1.length) {
if (Integer.parseInt(arr1[i]) != 0) {
return 1;
}
} else if (i < arr2.length) {
if (Integer.parseInt(arr2[i]) != 0) {
return -1;
}
}
i++;
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public static int specialCompare(String str1, String str2) {
String[] arr1 = str1.split("\\.");
String[] arr2 = str2.split("\\.");
for (int i = 1; i < arr1.length; i++) {
if (Integer.parseInt(arr1[i]) != 0) {
return 0;
}
}
for (int j = 1; j < arr2.length; j++) {
if (Integer.parseInt(arr2[j]) != 0) {
return 0;
}
}
if (arr1.length < arr2.length) {
return -1;
} else {
return 1;
}
}
// test
public static List<String> getLowerList(String str, Comparator<String> comparator, List<String> list) {
if (str == null) {
return list;
}
List<String> newlist = new ArrayList<String>();
newlist.add(str);
newlist.addAll(list);
// sort
Collections.sort(newlist, comparator);
// search
int endIndex = Collections.binarySearch(newlist, str);
if (endIndex >= 0) {
// sublist 0 1
return newlist.subList(0, endIndex + 1);
} else {
return new ArrayList<String>();
}
}
public static void main(String[] args) {
List<String> test1 = Arrays.asList(new String[]{
"2.1.1", "1.21.22", "1.21.25", "1.113", "0.0.0.1",
"2.0.0", "1.2", "2.0", "0.1", "1.2.1", "1.1.1",
"11", "100", "" + Integer.MAX_VALUE + ".1", "",
"2.0", "10.1"});
List<String> test2 = Arrays.asList(new String[]{"", null, "0", "10.20.100", "3.1.1", "9.8", "10.3.92"});
List<String> newlist = new ArrayList<String>();
newlist.addAll(test1);
newlist.addAll(test2);
Collections.sort(newlist, new VersionComparator());
VersionComparator compareVersion = new VersionComparator();
System.out.println(newlist);
System.out.println(getLowerList("2", compareVersion, newlist));
System.out.println(getLowerList("3", compareVersion, newlist));
System.out.println(getLowerList("4", compareVersion, newlist));
System.out.println(getLowerList("5", compareVersion, newlist));
}
}
Related
I am trying to create a simple pagination routine for values held in an ArrayList. Basically what I want to do is render the first five elements in the ArrayList at first go. And then when users click on Next (increment by another 5) and Previous (decrease by 5).
My logic looks like this:
class foo
{
private static final int defaultStep = 5;
private int moveCounter;
private List<String> values;
public foo()
{
values = new ArrayList<String>();
values.add("Fiber Channel");
values.add("Copper Channel");
...
}
private void pageNext()
{
if (moveCounter > -1 && moveCounter < values.size())
{
int currentIndex = (moveCounter + 1);
renderValues(currentIndex, false);
}
}
private void pagePrevious()
{
if (moveCounter > -1 && moveCounter <= values.size())
{
renderValues(moveCounter-1, true);
}
}
private void renderValues(int startIndex, boolean isPreviousCall)
{
if (startIndex > -1)
{
StringBuilder html = new StringBuilder();
List<String> valuesToRender = new ArrayList<String>();
int checkSteps = 1;
while (startIndex < values.size())
{
valuesToRender.add(values.get(startIndex));
if (checkSteps == defaultStep) break;
startIndex++;
checkSteps++;
}
moveCounter = startIndex;
//TODO: Build html String
...
}
}
}
I have an issue with pagePrevious call, can you guys help me build the valuesToRender 5-steps up values array before adding the value to render to the valuesToRender array.
I tried doing something like this also:
for (int start = startIndex, end = values.size() - 1; start < end; start++, end--)
{
if (isPreviousCall) valuesToRender.add(values.get(end));
else valuesToRender.add(values.get(start));
if (checkSteps == defaultStep) break;
checkSteps++;
}
But this doesn't seems to work neither. Can you guys spot and help me fix this issue.
Thanks Guys.
Based on "pscuderi" solution here
I've built a wrapping class that can be helpful for someone looking for this:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PaginatedList<T> {
private static final int DEFAULT_PAGE_SIZE = 10;
private List<T> list;
private List<List<T>> listOfPages;
private int pageSize = DEFAULT_PAGE_SIZE;
private int currentPage = 0;
public PaginatedList(List<T> list) {
this.list = list;
initPages();
}
public PaginatedList(List<T> list, int pageSize) {
this.list = list;
this.pageSize = pageSize;
initPages();
}
public List<T> getPage(int pageNumber) {
if (listOfPages == null ||
pageNumber > listOfPages.size() ||
pageNumber < 1) {
return Collections.emptyList();
}
currentPage = pageNumber;
List<T> page = listOfPages.get(--pageNumber);
return page;
}
public int numberOfPages() {
if (listOfPages == null) {
return 0;
}
return listOfPages.size();
}
public List<T> nextPage() {
List<T> page = getPage(++currentPage);
return page;
}
public List<T> previousPage() {
List<T> page = getPage(--currentPage);
return page;
}
public void initPages() {
if (list == null || listOfPages != null) {
return;
}
if (pageSize <= 0 || pageSize > list.size()) {
pageSize = list.size();
}
int numOfPages = (int) Math.ceil((double) list.size() / (double) pageSize);
listOfPages = new ArrayList<List<T>>(numOfPages);
for (int pageNum = 0; pageNum < numOfPages;) {
int from = pageNum * pageSize;
int to = Math.min(++pageNum * pageSize, list.size());
listOfPages.add(list.subList(from, to));
}
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 1; i <= 62; i++) {
list.add(i);
}
PaginatedList<Integer> paginatedList = new PaginatedList<Integer>(list);
while (true) {
List<Integer> page = paginatedList.nextPage();
if (page == null || page.isEmpty()) {
break;
}
for (Integer value : page) {
System.out.println(value);
}
System.out.println("------------");
}
}
}
Change
if (moveCounter > -1 && moveCounter <= archive.size())
{
renderValues(moveCounter-1, true);
}
to
if (moveCounter > 0 && moveCounter <= archive.size())
{
renderValues(moveCounter-1, true);
}
I would do it like this:
I'm not sure what renderValues does, and whether we have to substract 1 or maybe defaultStep from the upper bound of the moveCounter.
private void pageMove (int step)
{
moveCounter = moveCounter + step;
if (moveCounter < 0) moveCounter = 0;
if (moveCounter > values.size ()) moveCounter = values.size ();
renderValues (currentIndex, false);
}
private void pageNext ()
{
pageMove (defaultStep);
}
private void pagePrevious ()
{
pageMove (-defaultStep);
}
The first 3 lines could be packed into two big ternary experssions like so:
mc = ((mc + s) < 0) ? 0 : ((mc + s) > vs) ? vs : (mc + s);
but the 3 lines solution is better to follow.
Here is a simple java function for pagination. Note that the page starts from 0 (first page)
public List<Object> pagedResponse(List<Object> allItems, int page, int limit){
int totalItems = allItems.size();
int fromIndex = page*limit;
int toIndex = fromIndex+limit;
if(fromIndex <= totalItems) {
if(toIndex > totalItems){
toIndex = totalItems;
}
return allItems.subList(fromIndex, toIndex);
}else {
return Collections.emptyList();
}
}
I am trying to run a program from hackerrank. This is the question:
Sherlock considers a string to be valid if all characters of the string appear the same number of times. It is also valid if he can remove just one character at one index in the string, and the remaining characters will occur the same number of times. Given a string , determine if it is valid.
For example, if s="abc", it is a valid string because frequencies are {a:1,b:1,c:1}. So is abcc because we can remove one c and have 1 of each character in the remaining string. If s='abccc' however, the string is not valid as we can only remove 1 occurrence of c . That would leave character frequencies of {a:1,b:1,c:2}.
This is the link:
https://www.hackerrank.com/challenges/sherlock-and-valid-string/problem?h_l=interview&playlist_slugs%5B%5D=interview-preparation-kit&playlist_slugs%5B%5D=strings
One of the 5 test cases that failed would be:
aaaabbcc should give false, but it is giving me true.
aabbc should give true but is giving me false.
But somehow 5 of my test cases are coming to be wrong:
Here is the following program.
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
public class SherlokValidString
{
boolean is_valid(String s)
{
int count=0;
HashMap<Character, Integer> map = new HashMap<>();
char[] str_arr= s.toCharArray();
for(char c:str_arr)
{
if(map.containsKey(c))
{
map.put(c, map.get(c)+1);
}
else
{
map.put(c,1);
}
}
if (map.size()==1)
{
return true;
}
else {
List ll=new ArrayList<>(map.values());
System.out.println(ll);
Collections.sort(ll);
int first_element=(int)ll.get(0);
for(int i=1;i<(ll.size()-1);i++)
{
//System.out.println((int)ll.get(i)+1);
if (first_element==(int)ll.get(i+1))
{
count=0;
}
else if(first_element!=(int)ll.get(i+1))
{
count++;
}
}
if(count<=1)
{
//System.out.println(count);
return true;
}
else
{
return false;
}
}
}
public static void main(String[] args)
{
SherlokValidString svs = new SherlokValidString();
System.out.println(svs.is_valid("abbccc"));
}
}
It should be returning false, its giving me true.
Please help. Thanks.
With Java 8, one can do a very elegant job:
public static boolean sherlockStr(String s) {
// First, we're going to walk over the characters and count how many
// times each character occurs
Map<Character, Long> charOccurs = s.chars()
.mapToObj(t -> (char) t)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
// Then we're going to map each amount of characters found to its count,
// e.g. in the string "aabbcc" each character occurs twice → [2, 2, 2].
// That will yield a map: [2=3]
Map<Long, Long> counts = charOccurs.entrySet().stream()
.map(Map.Entry::getValue)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
switch (counts.size()) {
// If all characters occur an equal number of times, then the map
// contains just a single entry.
case 1:
return true;
// If there are two different amounts, then the difference between
// those two must be 1. Also, one of the numbers must occur exactly
// once.
case 2:
Iterator<Long> it = counts.keySet().iterator();
boolean diff = Math.abs(it.next() - it.next()) == 1;
return (diff && (counts.values().stream()
.anyMatch(i -> i == 1)));
// If the map's size is 3 or more, there are differents of amounts of
// characters.
default:
return false;
}
}
In short:
public static boolean sherlockStr(String s) {
Map<Long, Long> counts = s.chars()
.mapToObj(t -> (char) t)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet().stream()
.map(Map.Entry::getValue)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
switch (counts.size()) {
case 1:
return true;
case 2:
Iterator<Long> it = counts.keySet().iterator();
return (Math.abs(it.next() - it.next()) == 1 && (counts.values().stream()
.anyMatch(i -> i == 1)));
default:
return false;
}
}
I see the following fails in your logic:
The end bound of the last loop is wrong. It really does not matter starting at i==1 (for the first item is implicitly processed when initializing first_element), but the end bound must be ll.size(), not ll.size()-1.
The condition to decide if count must be incremented is too broad, and should be more restrictive: It is not enough that an appearing frequency be different from the lowest frequency (contained in first_element); it must be also ensured that it is, at least 1+first_element. And, if it is even higher, the loop must be broken and return false.
Try the following, see comments :
boolean isValid(String s)
{
HashMap<Character, Integer> map = new HashMap<>();
char[] str_arr= s.toCharArray();
for(char c:str_arr)
{
if(map.containsKey(c))
{
map.put(c, map.get(c)+1);
}
else
{
map.put(c,1);
}
}
//define min max frequencies
int min = (int) Math.pow(10, 5); int max = 0;
for(int value : map.values()) {
if (value < min ) {
min = value;
}
if(value > max ) {
max = value;
}
}
if(min == max) { return true;} //all frequencies equal
if( (max - min) > 1) {return false;} //removing one character can not make the equal
//for other cases make sure that only one frequency is different
int countBiggerThanMin = 0;
for(int value : map.values()) {
if(value > min ) {
countBiggerThanMin++;
}
}
if((countBiggerThanMin == 1) || //only one bigger than min
(countBiggerThanMin == (map.size() - 1))) { //min is samller than all others
return true;
}
return false;
}
By using java 8 Stream it becomes more concise :
boolean isValidForJava8(String s) {
Stream<String> stream = Stream.of(s.split(""));
Map<String, Long> map = stream.collect(
Collectors.groupingBy( Function.identity(), Collectors.counting() ));
Long min = map.values().stream().min(Long::compareTo).get();
Long max = map.values().stream().max(Long::compareTo).get();
if(min == max) { return true;} //all frequencies equal
if( (max - min) > 1) {return false;} //removing one character can not make the equal
//for other cases make sure that only one frequency is different
int countBiggerThanMin = map.values().stream().mapToInt(v -> (v > min) ? 1 : 0).sum();
if((countBiggerThanMin == 1) || //only one bigger than min
(countBiggerThanMin == (map.size() - 1))) { //min is samller than all others
return true;
}
return false;
}
This is a perfectly working solution:
{
if ((s.size() == 1) || (s.size() == 2))
return "YES";
std::map <char, int> hashValues;
for (char &c: s)
{
if (hashValues.count(c) != 0)
hashValues[c]++;
else
hashValues.insert(std::pair <char, int> (c,1));
}
int highest = 0;
int lowest = 100000;
for (map<char,int>::iterator it = hashValues.begin(); it != hashValues.end(); it++)
{
if (it->second < lowest)
lowest = it->second;
if (it->second > highest)
highest = it->second;
}
int countMin = 0;
int countMax = 0;
for (map<char,int>::iterator it = hashValues.begin(); it != hashValues.end(); it++)
{
if (it->second == highest)
countMax++;
if (it->second == lowest)
countMin++;
}
if ((highest - lowest) == 0) {return "YES";};
if (((highest - lowest) == 1) && (countMax == 1))
return "YES";
if (((highest - lowest) == 2))
return "NO";
if ((lowest == 1) && (countMin == 1))
return "YES";
return "NO";
}
Kotlin brings a bit of elegance =)
if (s.length == 1) return "YES"
val freq = s.groupingBy { it }.eachCount()
val max:Int = freq.maxBy { it.value }!!.value
val maxCount = freq.count { it.value == max }
val min = freq.minBy { it.value }!!.value
val minCount = freq.count { it.value == min }
val freqOfFreq = freq.map { it.value }.groupingBy { it }.eachCount()
return when {
freqOfFreq.size > 2 -> return "NO"
freqOfFreq.size == 1 -> return "YES"
minCount == 1 -> return "YES"
max - maxCount == min -> return "YES"
else -> "NO"
}
static List myCharList = new ArrayList<>();
static int isValidStringProcessor(String s) {
int returnStatus=-2;
List<Character> chars = s.chars().mapToObj(e -> (char)e).collect(Collectors.toList());
List<Integer> myList = new ArrayList<>();
Map<Character, Long> frequencyMap =
chars.stream().collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
for (Map.Entry<Character, Long> entry : frequencyMap.entrySet()) {
myList.add(entry.getValue().intValue());
myCharList.add(entry.getKey());
}
int min = Collections.min(myList);
int max = Collections.max(myList);
int minFrequency = Collections.frequency(myList, min);
int maxFrequency = Collections.frequency(myList, max);
if ((max==min)) {
returnStatus=-1;
} else if ((minFrequency==1 && maxFrequency==(myList.size()-1)) && (max-min==1)) {
returnStatus=myList.indexOf(min);
} else if ((minFrequency==(myList.size()-1) && maxFrequency==1) && (max-min==1)) {
returnStatus=myList.indexOf(max);
}
return returnStatus;
}
// Complete the isValid function below.
static String isValid(String s) {
String result ="NO";
int validIdenfitier = isValidStringProcessor(s);
if (validIdenfitier == -1) {
result="YES";
} else if (validIdenfitier >= 0) {
Character ch = myCharList.get(validIdenfitier);
String newString = s.replaceFirst(ch+"", "");
if (isValidStringProcessor(newString) == -1)
result="YES";
}
return result;
}
Try this it passes all test cases , similar to your approach. :)
static String isValid(String s) {
char c[]=s.toCharArray();
HashMap<Character,Integer> map=new HashMap<Character,Integer>();
for(char ch: c){
if(!map.containsKey(ch)){
map.put(ch,1);
}
else
map.put(ch,map.get(ch)+1);
}
Set<Integer> st = new HashSet<>();
for(int freq : map.values())
{
st.add(freq);
}
if(st.size() > 2)//More than 2 frequencies
System.out.println("NO");
else if(st.size() == 1)
return "YES";
else{
int f1 = 0;
int f2 = 0;
int f1Count = 0;
int f2Count = 0;
int i = 0;
for(int n : st)
{
if(i == 0) f1 = n;
else f2 = n;
i++;
}
for(int freq : map.values())
{
if(freq == f1) f1Count++;
if(freq == f2) f2Count++;
}
if((f1 == 1 && f1Count == 1 ) || (f2 == 1 && f2Count == 1 ))
return "YES";
else if ((Math.abs(f1 - f2) == 1) && (f1Count == 1 || f2Count == 1))
return "YES";
else
return "NO";
}
return "NO";
}
public String isValid(String s) {
HashMap<Character, Integer> trackFrequency = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
Character c = s.charAt(i);
if (trackFrequency.containsKey(c)) {
int count = trackFrequency.getOrDefault(c, 0);
count = count + 1;
trackFrequency.put(c, count);
} else {
trackFrequency.put(c, 1);
}
}
int sample = trackFrequency.get(s.charAt(0));
int unEqualFrequency = 0;
int unEqualValue = 0;
for (Integer value : trackFrequency.values()) {
if (sample != value) {
unEqualFrequency++;
}
if (unEqualFrequency == 1) {
unEqualValue = value;
}
}
if (unEqualFrequency > 1) {
return "NO";
} else if (Math.abs(sample - unEqualValue) <= 1 ||
unEqualFrequency <= 1) {
return "YES";
}
return "NO";
}
Here i am calculating the no of times the values are different in hashmap . There are three cases
if its == 0 > its sherlock string
if its >1 -> its not a Sherlock String
if its == 1 and the difference of the two values is ==1 -> its sherlock string
Here is the complete solution with all test cases passed in C#
public static string isValid(string s)
{
char[] charArray = s.ToCharArray();
char[] distinct = charArray.Distinct().ToArray();
Dictionary<char, int> result = new Dictionary<char, int>();
string ans = "NO";
if (charArray.Length != distinct.Length)
{
for (int i = 0; i < distinct.Length; i++)
{
var count = charArray.Where(x => x == distinct[i]).Count();
result.Add(distinct[i], count);
}
List<int> lcount = null;
lcount = result.Values.Distinct().ToList();
lcount.Sort();
lcount.Reverse();
if (lcount.Count > 1)
{
if (lcount.Count <= 2)
{
var diff = lcount[0] - lcount[1];
var a1 = result.Where(y => y.Value == lcount[0]).Count();
if (diff > 1 && lcount[1] != 1)
{
ans = "NO";
}
else
{
List<int> mcount = new List<int>();
foreach (var item in lcount)
{
var a = result.Where(y => y.Value == item).Count();
mcount.Add(a);
}
if (mcount.Count <= 2)
{
var fstValue = mcount[0];
var nextValue = mcount[1];
if (fstValue == 1 && (lcount[0] == 1 || diff == 1))
{
ans = "YES";
}
else if (nextValue == 1 && (lcount[1] == 1 || diff == 1))
{
ans = "YES";
}
}
}
}
}
else
ans = "YES";
}
else
ans = "YES";
return ans;
}
public class StringComparatorTest {
public static void main(String[] args) {
String[] a = {"abc9", "abc", "abc123", "ab9"};
String[] b = {"abc9", "abc", "abc123", "ab9"};
String[] c = {"abc9", "abc", "abc123", "ab9"};
System.out.print("a_Origin : ");
printArray(a);
System.out.print("c_Origin : ");
printArray(c);
System.out.print("a_Default : ");
Arrays.sort(a);
printArray(a);
System.out.print("c_Default : ");
Arrays.sort(c);
printArray(c);
System.out.print("a_Customized1: ");
Arrays.sort(a, new StringComparator());
printArray(a);
System.out.print("b_Customized1: ");
Arrays.sort(b, new StringComparator());
printArray(b);
System.out.print("c_Customized2: ");
Arrays.sort(c, new StringComparator2());
printArray(c);
}
public static void printArray(String[] arr){
for (String str: arr) {
System.out.print(str + " ");
}
System.out.println();
}
}
public class StringComparator implements Comparator {
#Override
public int compare(String s1, String s2) {
if(s1.length() == s2.length()){
if(s1.equals(s2))
return 0;
else{
for(int i = 0; i < s1.length(); i++){
if(s1.charAt(i) > s2.charAt(i)){
return 1;
}else {
return -1;
}
}
return 0;
}
}else if(s1.length() < s2.length()){
return -1;
}else{
return 1;
}
}
}
public class StringComparator2 implements Comparator {
#Override
public int compare(String s1, String s2) {
if (s1.length() == s2.length()) {
for (int i = 0; i < s1.length(); i++) {
if (s1.charAt(i) > s2.charAt(i)) {
return 1;
} else if (s1.charAt(i) < s2.charAt(i)) {
return -1;
}
}
return 0;
} else if (s1.length() < s2.length()) {
return -1;
} else {
return 1;
}
}
}
I have to comparators to sort a String objects, it turns out the "StringComparator" has some bugs but I can't figure out.
"StringComparator" works fine at
"Arrays.sort(b, new StringComparator());"
the out put order is as expected.
but when I use Default sort(steps below) and then sort by "StringComparator", the bug shows:
" Arrays.sort(a);
Arrays.sort(a, new StringComparator());"
the out put orders are different which should be the same.
(Array a and b are same)
Can someone explain a little bit?
Thanks a lot~
This works - compare in StringComparator class -
public int compare(String s1, String s2) {
if(s1.length() == s2.length()){
if(s1.equals(s2))
return 0;
else{
for(int i = 0; i < s1.length(); i++){
if(s1.charAt(i) != s2.charAt(i)) {
return s1.charAt(i) - s2.charAt(i);
}
}
}
}
return s1.length() - s2.length();
}
The result given for an array which is sorted first with default sort and then custom comparator(a_customized) is same as the on with using the custom comparator (b_customized)
The issue in old code seems to be here -
if(s1.charAt(i) > s2.charAt(i)){
return 1;
}else {
return -1;
}
if the s1.charAt(i) == s2.charAt(i) then also it was returning -1;
I know this problem is probably best served with DP, but I was wondering if it was possible to do it with recursion as a brute force way.
Given a set of words, say {"sales", "person", "salesperson"}, determine which words are compound (that is, it is the combination of 2 or more words in the list). So in this case, salesperson = sales + person, and is compound.
I based my answer heavily off of this problem: http://www.geeksforgeeks.org/dynamic-programming-set-32-word-break-problem/
public static void main(String args[]) throws Exception {
String[] test = { "salesperson", "sales", "person" };
String[] output = simpleWords(test);
for (int i = 0; i < output.length; i++)
System.out.println(output[i]);
}
static String[] simpleWords(String[] words) {
if (words == null || words.length == 0)
return null;
ArrayList<String> simpleWords = new ArrayList<String>();
for (int i = 0; i < words.length; i++) {
String word = words[i];
Boolean isCompoundWord = breakWords(words, word);
if (!isCompoundWord)
simpleWords.add(word);
}
String[] retVal = new String[simpleWords.size()];
for (int i = 0; i < simpleWords.size(); i++)
retVal[i] = simpleWords.get(i);
return retVal;
}
static boolean breakWords(String[] words, String word) {
int size = word.length();
if (size == 0 ) return true;
for (int j = 1; j <= size; j++) {
if (compareWords(words, word.substring(0, j)) && breakWords(words, word.substring(j, word.length()))) {
return true;
}
}
return false;
}
static boolean compareWords(String[] words, String word) {
for (int i = 0; i < words.length; i++) {
if (words[i].equals(word))
return true;
}
return false;
}
The problem here is now that while it successfully identifies salesperson as a compound word, it will also identify sales and person as a compound word. Can this code be revised so that this recursive solution works? I'm having trouble coming up with how I can easily do this.
Here is a solution with recursivity
public static String[] simpleWords(String[] data) {
List<String> list = new ArrayList<>();
for (String word : data) {
if (!isCompound(data, word)) {
list.add(word);
}
}
return list.toArray(new String[list.size()]);
}
public static boolean isCompound(String[] data, String word) {
return isCompound(data, word, 0);
}
public static boolean isCompound(String[] data, String word, int iteration) {
if (data == null || word == null || word.trim().isEmpty()) {
return false;
}
for (String str : data) {
if (str.equals(word) && iteration > 0) {
return true;
}
if (word.startsWith(str)) {
String subword = word.substring(str.length());
if (isCompound(data, subword, iteration + 1)) {
return true;
}
}
}
return false;
}
Just call it like this:
String[] data = {"sales", "person", "salesperson"};
System.out.println(Arrays.asList(simpleWords(data)));
I came across a post showing how to arrange char array by alphabet order.
seeing this can be done, I want to output the alphabetical order of each character of the input string, in order of the characters of the input string.
I'm a bit stuck. I can get the string reordered alphabetically, but I don't know what to do next.
example is 'monkey' to '354216'
because 'ekmnoy' e is alphabetically first from the set of given characters so e = 1 , k is the second alpha char when sorted so k = 2, and so on.
if you cannot understand I can provide more example to make things clear out.
Code
String str = "airport";
Character[] chars = new Character[str.length()];
for (int z = 0; z < chars.length; z++) {
chars[z] = str.charAt(z);
}
Arrays.sort(chars, new Comparator<Character>() {
public int compare(Character c1, Character c2) {
int cmp = Character.compare(
Character.toLowerCase(c1.charValue()),
Character.toLowerCase(c2.charValue()));
if (cmp != 0) {
return cmp;
}
return Character.compare(c1.charValue(), c2.charValue());
}
});
StringBuilder sb = new StringBuilder(chars.length);
for (char c : chars) {
sb.append(c);
}
str = sb.toString();
System.out.println(sb);
Output
aioprrt
expected output
Orange -> aegnOr
561432 - 123456
Monkey -> ekMnoy
354216 -> 123456
I dont know what you want to do with double characters, but if you add this few lines to your code at the end you are getting the right result. Iterate over the sorted String and replace the charakters in the original String with their indices in the sorted String.
String originalStr = "airport";
for(int i = 0; i<str.length(); i++) {
originalStr = originalStr.replace(str.charAt(i), String.valueOf(i+1).charAt(0));
}
System.out.println(originalStr);
Output: 1254357
If you want to get the output: 1254367 use replaceFirst:
originalStr = originalStr.replaceFirst(String.valueOf(str.charAt(i)), String.valueOf(i+1));
Input:Orange
Output:561432
Input:Monkey
Output:354216
The whole code:
String str = "airport";
String originalStr = str; //creat a backup of str because you change it in your code
Character[] chars = str.toCharArray();
Arrays.sort(chars, new Comparator<Character>() {
public int compare(Character c1, Character c2) {
int cmp = Character.compare(
Character.toLowerCase(c1.charValue()),
Character.toLowerCase(c2.charValue()));
if (cmp != 0) {
return cmp;
}
return Character.compare(c1.charValue(), c2.charValue());
}
});
str = String.valueOf(chars);
System.out.println(str);
//Iterate over the sorted String and replace the charakters in the original String with their indices in the sorted String
for(int i = 0; i<str.length(); i++) {
originalStr = originalStr.replaceFirst(String.valueOf(str.charAt(i)), String.valueOf(i+1));
}
System.out.println(originalStr);
Once you have arranged the characters in order (in a different array from the original) then create a third array by walking the original string and choosing the index of each character from te sorted string.
input: edcba
sorted: abcde
index: 01234
Pseudocode...
for( int i = 0; i < input.length(); i++ ) {
index[i] = sorted.indexOf(input[i]);
}
Result should be 43210 with the given input.
Note that strings with more than 10 characters will result in ambiguous output, which can be handled by inserting spaces in the output. Example:
abcdefghijk ->
012345678910
You can use this below code:
package Test;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class Arrange {
public static void main(String[] args) {
String str = "money";
List<Test> strs=new LinkedList<Test>();
List<Test> final_result=new LinkedList<Test>();
for(int i=0;i<str.length();i++)
{
Test t=new Test(i, ""+str.charAt(i), 0);
strs.add(t);
}
Collections.sort(strs,new Comparator<Test>() {
#Override
public int compare(Test o1, Test o2) {
return (o1.getS().compareToIgnoreCase(o2.getS()));
}
});
Integer i=1;
for (Test st : strs) {
st.setJ(i);
final_result.add(st);
i++;
}
Collections.sort(final_result,new Comparator<Test>() {
#Override
public int compare(Test o1, Test o2) {
return (o1.getI().compareTo(o2.getI()));
}
});
for (Test test : final_result) {
System.out.println(test.getJ());
}
}
}
class Test{
private Integer i;
private String s;
private Integer j;
public Test() {
// TODO Auto-generated constructor stub
}
public Test(Integer i, String s, Integer j) {
super();
this.i = i;
this.s = s;
this.j = j;
}
public Integer getI() {
return i;
}
public void setI(Integer i) {
this.i = i;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
public Integer getJ() {
return j;
}
public void setJ(Integer j) {
this.j = j;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((i == null) ? 0 : i.hashCode());
result = prime * result + ((j == null) ? 0 : j.hashCode());
result = prime * result + ((s == null) ? 0 : s.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Test other = (Test) obj;
if (i == null) {
if (other.i != null)
return false;
} else if (!i.equals(other.i))
return false;
if (j == null) {
if (other.j != null)
return false;
} else if (!j.equals(other.j))
return false;
if (s == null) {
if (other.s != null)
return false;
} else if (!s.equals(other.s))
return false;
return true;
}
}