I'm working on a Problem from CodeSignal:
Given a String s consisting of the alphabet only, return the first
non-repeated element. Otherwise, return '-'.
Example: input -
s="abacabad", output - 'c'.
I came up with the following the code. It passes only 16/19 test cases. Is there a way to solve this problem in O(n) or O(1)?
My code:
public char solution(String s) {
ArrayList<Character> hs = new ArrayList<>();
for (char c:s.toCharArray()) {
hs.add(c);
}
for (int j=0; j<s.length(); j++) {
if ( 1 == Collections.frequency(hs, s.charAt(j))) {
return s.charAt(j);
}
}
return '_';
}
The minimal possible time complexity for this task is linear O(n), because we need to examine every character in the given string to find out whether a particular character is unique.
Your current solution runs in O(n^2) - Collections.frequency() iterates over all characters in the string and this iteration and this method is called for every character. That's basically a brute-force implementation.
We can generate a map Map<Character,Boolean>, which associates each character with a boolean value denoting whether it's repeated or not.
That would allow to avoid iterating over the given string multiple times.
Then we need to iterate over the key-set to find the first non-repeated character. As the Map implementation LinkedHashMap is used to ensure that returned non-repeated character would be the first encountered in the given string.
To update the Map I've used Java 8 method merge(), which expects three arguments: a key, a value, and a function responsible for merging the old value and the new one.
public char solution(String s) {
Map<Character, Boolean> isNonRepeated = getMap(s);
for (Map.Entry<Character, Boolean> entry: isNonRepeated.entrySet()) {
if (entry.getValue()) {
return entry.getKey();
}
}
return '_';
}
public Map<Character, Boolean> getMap(String s) {
Map<Character, Boolean> isNonRepeated = new LinkedHashMap<>();
for (int i = 0; i < s.length(); i++) {
isNonRepeated.merge(s.charAt(i), true, (v1, v2) -> false);
}
return isNonRepeated;
}
In case if you're comfortable with streams, this problem can be addressed in one statement (the algorithm remains the same and time complexity would be linear as well):
public char solution(String s) {
return s.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.toMap( // creates intermediate Map<Character, Boolean>
Function.identity(), // key
c -> true, // value - first occurrence, character is considered to be non-repeated
(v1, v2) -> false, // resolving values, character is proved to be a duplicate
LinkedHashMap::new
))
.entrySet().stream()
.filter(Map.Entry::getValue)
.findFirst()
.map(Map.Entry::getKey)
.orElse('_');
}
Here is a slightly different approach using both a Set to account for duplicates, and a Queue to hold candidates before a possible duplicate is discovered.
iterate over the list of characters.
try and add the character to the seen set. If not already there,
also add it to the candidates queue.
else if it has been "seen", try and remove it from the candidates queue.
By the time this gets done, the head of the queue should contain the first, non-repeating character. If the queue is empty, return the default value as no unique character was found.
public char solution(String s) {
Queue<Character> candidates = new LinkedList<>();
Set<Character> seen = new HashSet<>();
for (char c : s.toCharArray()) {
if (seen.add(c)) {
candidates.add(c);
} else {
candidates.remove(c);
}
}
return candidates.isEmpty() ? '_' : candidates.peek();
}
I have done pretty extensive testing of this and it has yet to fail. It is also comparatively very efficient. But as can happen, I may have overlooked something.
One technique would be a 2 pass solution using a frequency/count array for each character.
public static char firstNonRepeatingChar(String s) {
int[] frequency = new int[26]; // this is O(1) space complexity because alphabet is finite of 26 letters
/* First Pass - Fill our frequency array */
for(int i = 0; i < s.length(); i++) {
frequency[s.charAt(i) - 'a']++;
}
/* Second Pass - Look up our frequency array */
for(int i = 0; i < s.length(); i++) {
if(frequency[s.charAt(i) - 'a'] == 1) {
return s.charAt(i);
}
}
/* Not Found */
return '_';
}
This solution is O(2n) -> O(n) and a space complexity of O(1) because we are using a finite set of the English alphabet (26 letters). This wouldn't work in other scenarios for non-English alphabets.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I want to make a system that when written a letter (example:a) it converts it to a symbols (example:ԅ).
How do I really do it? I know it seems simple, but I am very, very new into programming, all I can do is a simple calendar or a calculator.
I've tried to do it alone, but got nowhere. I type a whole word but it just prints the first letter instead of the word, that contains 4+. For example I type "aura" but it prints only "ԅ"
THANKS A LOT TO ALL OF YOU!!! I FINALLY MADE IT!!!!
Use string = string.replace("a", "ԅ");
where string is the given word (in your case, aura).
You can use a HashMap<Character, Character> in this case!
If you don't know what that is, it's basically a "dictionary". You can just make the HashMap like this in your case:
a -> ԅ
b -> !
c -> #
d -> #
Note: The above is not real code, just a visual representation! And I don't know what your symbols mean and how to type them, so I used normal symbols
See? It's really a dictionary. When you look for "a", you get "ԅ", when you look for "b", you get "!", etc. Do you get it now?
Okay, now let's see the code
HashMap<Character, Character> characterMap = new HashMap<> (); //Create a new HashMap
characterMap.put ('a', 'ԅ'); // Put an entry in the "dictionary"
characterMap.put ('b', '!');
//and so on... I am lazy
Now suppose you are able to get the user input and stored it in a string called input. Just like Anthony's code, you do this:
for (Map.Entry<Character, Character> entry : map.entrySet()) {
input = input.replace(entry.getKey(), entry.getValue());
}
This is basically saying that,
Go to find every entry in the dictionary.
For each entry,
Store it in the "entry" variable
Replace all the occurrences of entry's key by entry's value
Hi #EpicJavaNoob and welcome on StackOverflow.
You could solve your problem by using a Map and replace() function on Strings.
You have to create this class in your program.
public class MyReplacer {
private static final Map<Character, Character> map = new HashMap<Character, Character>();
static { // the static block is used to instantiate the Map once for all
map.put('a' , '\u2026');
map.put('b', '+');
}
public static String encode(String stringToEncode) {
// for each entry in the hashmap, we replace the character by his corresponding key
for (Map.Entry<Character, Character> entry : map.entrySet()) {
stringToEncode = stringToEncode.replace(entry.getKey(), entry.getValue());
}
return stringToEncode;
}
}
Then in your code you can use the previous class this way :
public static void main(String[] args) {
String str = "aura";
String encodedStr = MyReplacer.encode(str);
}
A Map is use to associate a key to a value.
I do agree that this is not the simple solution you can find, but this is how you'l learn coding, looking at code wrote by other and try to understand how it works, and why it has ben wrote this way.
I'd use a for loop that reads the string supplied one letter at a time then makes a new string that "translates" every individual letter.
Here's some quick code:
Scanner i = new Scanner(System.in);
System.out.print("Enter string:");
String start = i.next();
int len = start.length;
String end = "";
for (int a = 0, a<len, a++) {
if (start.Charat(a) == "a") {
end = end.append(ԅ)
} etc...
}
I just made this off the top of my head, I'll try to make a working version if you give me more substitutions :)
The best way is to code a Translator class with a translation table:
Edit: I've chosen the Map alternative instead of the char[] one (my first thought), after agreeing with AnthonyRaymond's suggest.
public class Translator
{
private Map<Character,Character> table=createTable();
public String translate(String input)
{...}
}
To construct the table, you must set just the explicit mappings (in order to reduce the table to the minimum size):
private Map<Character,Character> createTable()
{
Map<Character,Character> table=new HashMap<Character,Character>();
// Set the explicit mappings:
table.put('a', 'ԅ');
table.put('b', ...);
return table;
}
The translation rutine must first index the table, and if the character is not found, select the input character (in order to leave untranslated the blanks, newlines and rest of non-translatable characters):
public String translate(String input)
{
StringBuilder stb=new StringBuilder(input.length());
for (int i=0;i<input.length();i++)
{
char inputChar=input.charAt(i);
Character outputChar=table.get(inputChar);
if (outputChar==null)
{
outputChar=inputChar;
}
stb.append(outputChar);
}
return stb.toString();
}
I have a ArrayList, with elements something like:
[string,has,was,hctam,gnirts,saw,match,sah]
I would like to delete the ones which are repeating itself, such as string and gnirts, and delete the other(gnirts). How do I go about achieving something as above?
Edit: I would like to rephrase the question:
Given an arrayList of strings, how does one go about deleting elements containing reversed strings?
Given the following input:
[string,has,was,hctam,gnirts,saw,match,sah]
How does one reach the following output:
[string,has,was,match]
Set<String> result = new HashSet<String>();
for(String word: words) {
if(result.contains(word) || result.contains(new StringBuffer(word).reverse().toString())) {
continue;
}
result.add(word);
}
// result
You can use a comparator that sorts the characters before checking them for equality. This means that compare("string", "gnirts") will return 0. Then use this comparator as you traverse through the list and copy the matching elements to a new list.
Another option (if you have a really large list) is to create an Anagram class that extends the String class. Override the hashcode method so that anagrams produce the same hashcode, then use a hashmap of anagrams to check your array list for anagrams.
HashSet<String> set = new HashSet<String>();
for (String str : arraylst)
{
set.add(str);
}
ArrayList<String> newlst = new ArrayList<String>();
for (String str : arraylst)
{
if(!set.contains(str))
newlst.add(str);
}
To remove duplicate items, you can use HashMap (), where as the key codes will be used by the sum of the letters (as each letter has its own code - is not a valid situation where two different words have an identical amount of code numbers), as well as the value - this the word. When adding a new word in a HashMap, if the amount of code letters of new words is identical to some of the existing key in a HashMap, then the word with the same key is replaced by a new word. Thus, we get the HashMap collection of words without repetition.
With regard to the fact that the bottom line "string" looks better "gnirts". It may be a situation where we can not determine which word is better, so the basis has been taken that the final form of the word is not important - thing is that there are no duplicate
ArrayList<String> mainList = new ArrayList<String>();
mainList.add("string,has,was,hctam,gnirts,saw,match,sah");
String[] listChar = mainList.get(0).split(",");
HashMap <Integer, String> hm = new HashMap<Integer, String>();
for (String temp : listChar) {
int sumStr=0;
for (int i=0; i<temp.length(); i++)
sumStr += temp.charAt(i);
hm.put(sumStr, temp);
}
mainList=new ArrayList<String>();
Set<Map.Entry<Integer, String>> set = hm.entrySet();
for (Map.Entry<Integer, String> temp : set) {
mainList.add(temp.getValue());
}
System.out.println(mainList);
UPD:
1) The need to maintain txt-file in ANSI
In the beginning, I replaced Scaner on FileReader and BufferedReader
String fileRStr = new String();
String stringTemp;
FileReader fileR = new FileReader("text.txt");
BufferedReader streamIn = new BufferedReader(fileR);
while ((stringTemp = streamIn.readLine()) != null)
fileRStr += stringTemp;
fileR.close();
mainList.add(fileRStr);
In addition, all the words in the file must be separated by commas, as the partition ishonoy lines into words by the function split (",").
If you have words separated by another character - replace the comma at the symbol in the following line:
String[] listChar = mainList.get(0).split(",");
During my work with databases I noticed that I write query strings and in this strings I have to put several restrictions in the where-clause from a list/array/collection. Should look like this:
select * from customer
where customer.id in (34, 26, ..., 2);
You can simplify this by reducing this to the question that you have collection of strings and want to create a comma-separated list of this strings in just one string.
My approach I have used so far is something like that:
String result = "";
boolean first = true;
for(String string : collectionOfStrings) {
if(first) {
result+=string;
first=false;
} else {
result+=","+string;
}
}
But this is as you can see very ugly. You cannot see what happens there on the first look, especially when the constructed strings (like every SQL query) is getting complicated.
What is your (more) elegant way?
Use the Google Guava API's join method:
Joiner.on(",").join(collectionOfStrings);
Note: This answers was good when it was written 11 years ago, but now there are far better options to do this more cleanly in a single line, both using only Java built-in classes or using a utility library. See other answers below.
Since strings are immutable, you may want to use the StringBuilder class if you're going to alter the String in the code.
The StringBuilder class can be seen as a mutable String object which allocates more memory when its content is altered.
The original suggestion in the question can be written even more clearly and efficiently, by taking care of the redundant trailing comma:
StringBuilder result = new StringBuilder();
for(String string : collectionOfStrings) {
result.append(string);
result.append(",");
}
return result.length() > 0 ? result.substring(0, result.length() - 1): "";
I just looked at code that did this today. This is a variation on AviewAnew's answer.
collectionOfStrings = /* source string collection */;
String csList = StringUtils.join(collectionOfStrings.toArray(), ",");
The StringUtils ( <-- commons.lang 2.x, or commons.lang 3.x link) we used is from Apache Commons.
The way I write that loop is:
StringBuilder buff = new StringBuilder();
String sep = "";
for (String str : strs) {
buff.append(sep);
buff.append(str);
sep = ",";
}
return buff.toString();
Don't worry about the performance of sep. An assignment is very fast. Hotspot tends to peel off the first iteration of a loop anyway (as it often has to deal with oddities such as null and mono/bimorphic inlining checks).
If you use it lots (more than once), put it in a shared method.
There is another question on stackoverflow dealing with how to insert a list of ids into an SQL statement.
Since Java 8, you can use:
String String.join(CharSequence delimiter, CharSequence... elements)
String String.join(CharSequence delimiter, Iterable<? extends CharSequence> elements)
If you want to take non-Strings and join them to a String, you can use Collectors.joining(CharSequence delimiter), e.g.:
String joined = anyCollection.stream().map(Object::toString).collect(Collectors.joining(","));
I found the iterator idiom elegant, because it has a test for more elements (ommited null/empty test for brevity):
public static String convert(List<String> list) {
String res = "";
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
res += iterator.next() + (iterator.hasNext() ? "," : "");
}
return res;
}
There's a lot of manual solutions to this, but I wanted to reiterate and update Julie's answer above. Use google collections Joiner class.
Joiner.on(", ").join(34, 26, ..., 2)
It handles var args, iterables and arrays and properly handles separators of more than one char (unlike gimmel's answer). It will also handle null values in your list if you need it to.
String.join(", ", collectionOfStrings)
available in the Java8 api.
alternative to (without the need to add a google guava dependency):
Joiner.on(",").join(collectionOfStrings);
Here's an incredibly generic version that I've built from a combination of the previous suggestions:
public static <T> String buildCommaSeparatedString(Collection<T> values) {
if (values==null || values.isEmpty()) return "";
StringBuilder result = new StringBuilder();
for (T val : values) {
result.append(val);
result.append(",");
}
return result.substring(0, result.length() - 1);
}
You could try
List collections = Arrays.asList(34, 26, "...", 2);
String asString = collection.toString();
// justValues = "34, 26, ..., 2"
String justValues = asString.substring(1, asString.length()-1);
This will be the shortest solution so far, except of using Guava or Apache Commons
String res = "";
for (String i : values) {
res += res.isEmpty() ? i : ","+i;
}
Good with 0,1 and n element list. But you'll need to check for null list.
I use this in GWT, so I'm good without StringBuilder there. And for short lists with just couple of elements its ok too elsewhere ;)
In case someone stumbled over this in more recent times, I have added a simple variation using Java 8 reduce(). It also includes some of the already mentioned solutions by others:
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import com.google.common.base.Joiner;
public class Dummy {
public static void main(String[] args) {
List<String> strings = Arrays.asList("abc", "de", "fg");
String commaSeparated = strings
.stream()
.reduce((s1, s2) -> {return s1 + "," + s2; })
.get();
System.out.println(commaSeparated);
System.out.println(Joiner.on(',').join(strings));
System.out.println(StringUtils.join(strings, ","));
}
}
In Android you should use this:
TextUtils.join(",",collectionOfStrings.toArray());
I think it's not a good idea contruct the sql concatenating the where clause values like you are doing :
SELECT.... FROM.... WHERE ID IN( value1, value2,....valueN)
Where valueX comes from a list of Strings.
First, if you are comparing Strings they must be quoted, an this it isn't trivial if the Strings could have a quote inside.
Second, if the values comes from the user,or other system, then a SQL injection attack is possible.
It's a lot more verbose but what you should do is create a String like this:
SELECT.... FROM.... WHERE ID IN( ?, ?,....?)
and then bind the variables with Statement.setString(nParameter,parameterValue).
Just another method to deal with this problem. Not the most short, but it is efficient and gets the job done.
/**
* Creates a comma-separated list of values from given collection.
*
* #param <T> Value type.
* #param values Value collection.
* #return Comma-separated String of values.
*/
public <T> String toParameterList(Collection<T> values) {
if (values == null || values.isEmpty()) {
return ""; // Depending on how you want to deal with this case...
}
StringBuilder result = new StringBuilder();
Iterator<T> i = values.iterator();
result.append(i.next().toString());
while (i.hasNext()) {
result.append(",").append(i.next().toString());
}
return result.toString();
}
There are some third-party Java libraries that provide string join method, but you probably don't want to start using a library just for something simple like that. I would just create a helper method like this, which I think is a bit better than your version, It uses StringBuffer, which will be more efficient if you need to join many strings, and it works on a collection of any type.
public static <T> String join(Collection<T> values)
{
StringBuffer ret = new StringBuffer();
for (T value : values)
{
if (ret.length() > 0) ret.append(",");
ret.append(value);
}
return ret.toString();
}
Another suggestion with using Collection.toString() is shorter, but that relies on Collection.toString() returning a string in a very specific format, which I would personally not want to rely on.
If you use Spring, you can do:
StringUtils.arrayToCommaDelimitedString(
collectionOfStrings.toArray()
)
(package org.springframework.util)
List<String> collectionOfStrings = // List of string to concat
String csvStrings = StringUtils.collectionToDelimitedString(collectionOfStrings, ",");
StringUtils from springframeowrk:spring-core
I'm not sure how "sophisticated" this is, but it's certainly a bit shorter. It will work with various different types of collection e.g. Set<Integer>, List<String>, etc.
public static final String toSqlList(Collection<?> values) {
String collectionString = values.toString();
// Convert the square brackets produced by Collection.toString() to round brackets used by SQL
return "(" + collectionString.substring(1, collectionString.length() - 1) + ")";
}
Exercise for reader: modify this method so that it correctly handles a null/empty collection :)
What makes the code ugly is the special-handling for the first case. Most of the lines in this small snippet are devoted, not to doing the code's routine job, but to handling that special case. And that's what alternatives like gimel's solve, by moving the special handling outside the loop. There is one special case (well, you could see both start and end as special cases - but only one of them needs to be treated specially), so handling it inside the loop is unnecessarily complicated.
I've just checked-in a test for my library dollar:
#Test
public void join() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
String string = $(list).join(",");
}
it create a fluent wrapper around lists/arrays/strings/etc using only one static import: $.
NB:
using ranges the previous list can be re-writed as $(1, 5).join(",")
The nice thing about the IN expression is that if you have repeated values, it does not change the result. So, just duplicate the first item and process the entire list. This assumes that there is at least one item in the list. If there are no items, I'd suggest checking for that first and then not executing the SQL at all.
This will do the trick, is obvious in what it is doing and does not rely on any external libraries:
StringBuffer inString = new StringBuffer(listOfIDs.get(0).toString());
for (Long currentID : listOfIDs) {
inString.append(",").append(currentID);
}
While I think your best bet is to use Joiner from Guava, if I were to code it by hand I find this approach more elegant that the 'first' flag or chopping the last comma off.
private String commas(Iterable<String> strings) {
StringBuilder buffer = new StringBuilder();
Iterator<String> it = strings.iterator();
if (it.hasNext()) {
buffer.append(it.next());
while (it.hasNext()) {
buffer.append(',');
buffer.append(it.next());
}
}
return buffer.toString();
}
if you have an array you can do:
Arrays.asList(parameters).toString()
Another option, based on what I see here (with slight modifications).
public static String toString(int[] numbers) {
StringBuilder res = new StringBuilder();
for (int number : numbers) {
if (res.length() != 0) {
res.append(',');
}
res.append(number);
}
return res.toString();
}
Join 'methods' are available in Arrays and the classes that extend AbstractCollections but doesn't override toString() method (like virtually all collections in java.util).
For instance:
String s= java.util.Arrays.toString(collectionOfStrings.toArray());
s = s.substing(1, s.length()-1);// [] are guaranteed to be there
That's quite weird way since it works only for numbers alike data SQL wise.
You may be able to use LINQ (to SQL), and you may be able to make use of the Dynamic Query LINQ sample from MS. http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
java.util.List<String> lista = new java.util.ArrayList<String>();
lista.add("Hola");
lista.add("Julio");
System.out.println(lista.toString().replace('[','(').replace(']',')'));
$~(Hola, Julio)
String commaSeparatedNames = namesList.toString().replaceAll( "[\\[|\\]| ]", "" ); // replace [ or ] or blank
The string representation consists of a list of the collection's
elements in the order they are returned by its iterator, enclosed in
square brackets ("[]"). Adjacent elements are separated by the
characters ", " (comma and space).
AbstractCollection javadoc
List token=new ArrayList(result);
final StringBuilder builder = new StringBuilder();
for (int i =0; i < tokens.size(); i++){
builder.append(tokens.get(i));
if(i != tokens.size()-1){
builder.append(TOKEN_DELIMITER);
}
}
builder.toString();