First non-repeating character in a stream of characters - java

The question asks us to find new string B.
B is formed such that we have to find first non-repeating character each time a character is inserted to the stream and append it at the end to B. If no non-repeating character is found then append '#' at the end of B.
Example:
"a" - first non repeating character 'a'
"ab" - first non repeating character 'a'
"aba" - first non repeating character 'b'
"abad" - first non repeating character 'b'
"abadb" - first non repeating character 'd'
"abadbc" - first non repeating character 'd'
Can someone help me out where my code went wrong. my logic is to use substring function of string and find the unique character and add it to arraylist and print the entire arraylist.
public class Solution
{
public String solve(String A)
{
ArrayList<Character>a=new ArrayList<Character>();
String res="";
for(int i=0;i<A.length();i++)
{
String ss=A.substring(0,i+1);
String ue=uniqueCharacters(ss);
// System.out.println(ue);
if(ue!="") a.add(ue.charAt(0));
else a.add('#');
}
for(Character j:a) res+=j;
return res;
}
public static String uniqueCharacters(String test)
{
String temp = "";
for (int i = 0; i < test.length(); i++)
{
char current = test.charAt(i);
if (temp.indexOf(current) < 0) temp = temp + current;
else temp = temp.replace(String.valueOf(current), "");
}
return temp;
}
}

It may be better to use a Set to detect unique characters in the input string using the result of Set::add which returns false if no element has been actually added to the set, and a Queue to maintain the non-repeated characters.
When a repeated (non-unique) character is detected, it gets removed from the queue and if necessary, "#" is applied as a placeholder. And if a unique character is detected, it is just added to the queue.
Example implementation:
public static String solve(String str) {
// verify the input
if (null == str || str.isEmpty()) {
return str;
}
Set<Character> previous = new LinkedHashSet<>();
Queue<Character> nonRepeated = new LinkedList<>();
StringBuilder sb = new StringBuilder();
// use the first character
char curr = str.charAt(0);
previous.add(curr);
nonRepeated.add(curr);
sb.append(curr);
for (int i = 1, n = str.length(); i < n; i++) {
char c = str.charAt(i);
if (!previous.add(c)) { // duplicate character found
nonRepeated.remove(c);
if (nonRepeated.isEmpty()) {
curr = '#';
} else { // get the next non-repeated character
curr = nonRepeated.peek();
}
} else { // unique element is detected
if (curr == '#') {
curr = c;
}
nonRepeated.add(c);
}
sb.append(curr);
}
return sb.toString();
}
Tests:
for (String t : Arrays.asList("abadbc", "abcdba", "aaabbbacab")) {
System.out.printf("input: %s --> %s%n----%n", t, solve(t));
}
Output:
input: abadbc --> aabbdd
----
input: abcdba --> aaaaac
----
input: aaabbbacab --> a##b###ccc
----

When you encounter a char a 2nd time you remove it from the unique characters (1), but if there was a 3rd of that character it would be added again (2).
public static String uniqueCharacters(String test)
{
String temp = "";
for (int i = 0; i < test.length(); i++)
{
char current = test.charAt(i);
if (temp.indexOf(current) < 0) temp = temp + current; <---- (2)
else temp = temp.replace(String.valueOf(current), ""); <----- (1)
}
return temp;
}
Solution count the number of chars and then return only those whose count is 1 (one).

Related

Reverse words without changing capitals or punctuation

Create a program with the lowest amount of characters to reverse each word in a string while keeping the order of the words, as well as punctuation and capital letters, in their initial place.
By "Order of the words", I mean that each word is split by an empty space (" "), so contractions and such will be treated as one word. The apostrophe in contractions should stay in the same place. ("Don't" => "Tno'd").
(Punctuation means any characters that are not a-z, A-Z or whitespace*).
Numbers were removed from this list due to the fact that you cannot have capital numbers. Numbers are now treated as punctuation.
For example, for the input:
Hello, I am a fish.
it should output:
Olleh, I ma a hsif.
Notice that O, which is the first letter in the first word, is now capital, since H was capital before in the same location.
The comma and the period are also in the same place.
More examples:
This; Is Some Text!
would output
Siht; Si Emos Txet!
I've tried this:
public static String reverseWord(String input)
{
String words[]=input.split(" ");
StringBuilder result=new StringBuilder();
for (String string : words) {
String revStr = new StringBuilder(string).reverse().toString();
result.append(revStr).append(" ");
}
return result.toString().trim();
}
I have tried to solve your problem. It's working fine for the examples I have checked :) Please look and let me know :)
public static void main(String[] args) {
System.out.println(reverseWord("This; Is Some Text!"));
}
public static boolean isAlphaNumeric(String s) {
return s != null && s.matches("^[a-zA-Z0-9]*$");
}
public static String reverseWord(String input)
{
String words[]=input.split(" ");
StringBuilder result=new StringBuilder();
int startIndex = 0;
int endIndex = 0;
for(int i = 0 ; i < input.length(); i++) {
if (isAlphaNumeric(Character.toString(input.charAt(i)))) {
endIndex++;
} else {
String string = input.substring(startIndex, endIndex);
startIndex = ++endIndex;
StringBuilder revStr = new StringBuilder("");
for (int j = 0; j < string.length(); j++) {
char charToAdd = string.charAt(string.length() - j - 1);
if (Character.isUpperCase(string.charAt(j))) {
revStr.append(Character.toUpperCase(charToAdd));
} else {
revStr.append(Character.toLowerCase(charToAdd));
}
}
result.append(revStr);
result.append(input.charAt(i));
}
}
if(endIndex>startIndex) // endIndex != startIndex
{
String string = input.substring(startIndex, endIndex);
result.append(string);
}
return result.toString().trim();
}
Call the reverseWord with your test string.
Hope it helps. Don't forget to mark it as right answer, if it is :)
Here is a proposal that follows your requirements. It may seem very long but its just comments and aerated code; and everybody loves comments.
public static String smartReverseWords(String input) {
StringBuilder finalString = new StringBuilder();
// Word accumulator, resetted after each "punctuation" (or anything different than a letter)
StringBuilder wordAcc = new StringBuilder();
int processedChars = 0;
for(char c : input.toCharArray()) {
// If not a whitespace nor the last character
if(!Character.isWhitespace(c)) {
// Accumulate letters
wordAcc.append(c);
// Have I reached the last character? Then finalize now:
if(processedChars == input.length()-1) {
reverseWordAndAppend(wordAcc, finalString);
}
}
else {
// Was a word accumulated?
if(wordAcc.length() > 0) {
reverseWordAndAppend(wordAcc, finalString);
}
// Append non-letter char to final string:
finalString.append(c);
}
processedChars++;
}
return finalString.toString();
}
private static void reverseWordAndAppend(StringBuilder wordAcc, StringBuilder finalString) {
// Then reverse it:
smartReverse(wordAcc); // a simple wordAcc.reverse() is not possible
// Append word to final string:
finalString.append(wordAcc.toString());
// Reset accumulator
wordAcc.setLength(0);
}
private static class Marker {
Integer position;
String character;
}
private static void smartReverse(StringBuilder wordAcc) {
char[] arr = wordAcc.toString().toCharArray();
wordAcc.setLength(0); // clean it for now
// Memorize positions of 'punctuation' + build array free of 'punctuation' in the same time:
List<Marker> mappedPosOfNonLetters = new ArrayList<>(); // order matters
List<Integer> mappedPosOfCapitals = new ArrayList<>(); // order matters
for (int i = 0; i < arr.length; i++) {
char c = arr[i];
if(!Character.isLetter(c)) {
Marker mark = new Marker();
mark.position = i;
mark.character = c+"";
mappedPosOfNonLetters.add(mark);
}
else {
if(Character.isUpperCase(c)) {
mappedPosOfCapitals.add(i);
}
wordAcc.append(Character.toLowerCase(c));
}
}
// Reverse cleansed word:
wordAcc.reverse();
// Reintroduce 'punctuation' at right place(s)
for (Marker mark : mappedPosOfNonLetters) {
wordAcc.insert(mark.position, mark.character);
}
// Restore capitals at right place(s)
for (Integer idx : mappedPosOfCapitals) {
wordAcc.setCharAt(idx,Character.toUpperCase(wordAcc.charAt(idx)));
}
}
EDIT
I've updated the code to take all your requirements into account. Indeed we have to make sure that "punctuation' stay in place (and capitals also) but also within a word, like a contraction.
Therefore given the following input string:
"Hello, I am on StackOverflow. Don't tell anyone."
The code produces this output:
"Olleh, I ma no WolfrEvokcats. Tno'd llet enoyna."

Count of distinct duplicated characters in Java

I've written code to find the amount of duplicated characters in a string input. However after reading the specification, I realise this isn't what is being asked. Yes, I need to count the duplicated characters, but not the amount of that particular character, I need the amount of all characters being duplicated.
I've given an input and output to help explain better. I don't think I'm doing a good job.
Input:
"abcdea" => "a" is duplicated =>
Output:
1
Input:
"abcdeae" => "a" and "e" is duplicated =>
Output:
2
Input:
"abcdeaed" => "a", "d" and "e" is duplicated =>
Output:
3
I've put my code below. Can anyone help adjust my code please?
public static int duplicatesCount(String text)
{
Map<Character,Integer> map = new HashMap<Character,Integer>();
char[] carray = text.toCharArray();
for (char c : carray)
{
if (map.containsKey(c))
{
map.put(c, map.get(c) +1);
}
else
{
map.put(c, 1);
}
}
Set <Character> setChar = map.keySet();
int returnC = 1;
for (Character c : setChar)
{
if (map.get(c) > 1)
{
returnC = map.get(c);
}
}
return returnC;
A better way to do this is to sort the string and then iterate through it. If the previous character = the current character, you increase the duplicate number and don't increment it again util you see the character change.
This requires no extra storage (e.g. the hash map).
This can also be used to count the number of duplicates of each letter with a minor change :).
At the end of your snippet...
int returnC = 0;
for (Character c : setChar)
{
if (map.get(c) > 1)
{
returnC ++; //for each which has more than one instance
}
}
return returnC;
Just count everytime you come across a character that has a value greater than 1 in your hashmap
int returnC = 0;
for (Character c : setChar)
{
if (map.get(c) > 1)
returnC++;
}
Or you can do it while you're creating your hashmap like this
Map<Character,Integer> map = new HashMap<Character,Integer>();
char[] carray = text.toCharArray();
Set <Character> setChar = new Set<Character>(); //initialize the set up here
for (char c : carray)
{
if (map.containsKey(c))
{
map.put(c, map.get(c) +1);
setChar.add(c); //just add to set when a char already exists
}
else
{
map.put(c, 1);
}
}
return setChar.size(); //then simply return the size of the set
Here is another approach - without using Map or sorting algorithm.
public static int duplicatesCount(String text) {
int cnt = 0;
while (text.length() > 1) {
char firstLetter = text.charAt(0);
if (text.lastIndexOf(firstLetter) > 0) {
cnt++;
text = text.replaceAll(("" + firstLetter), "");
} else {
text = text.substring(1, text.length());
}
}
return cnt;
}
The basic idea is to go through a string, char-by-char and check does the character exist somewhere in the string ( if (text.lastIndexOf(firstLetter) > 0)). If it exists increment cnt and then remove all occurrences of the character in the string.
Otherwise, just remove the fist character (text = text.substring(1, text.length());)
You could use String methods
int count = 0;
String s = "abcdeabc";
while(s.length() > 0) {
String c = s.substring(0, 1);
if(s.lastIndexOf(c) != 0) {
count++;
}
s = s.replaceAll(c, "");
}
In your current code you are returning the value not the count.
For Eg:
If your input was "abcdea", then returnC holds the value 2 which is the
number of times the key a has repeated.
If your input was "abcdabeb" where a is repeated twice and b is repeated
thrice. Your output will be 3, as returnC will be holding the value for the key
b.
You can modify your code like this.
Set <Character> setChar = map.keySet();
int returnC = 0;
for (Character c : setChar) {
if (map.get(c) > 1) {
returnC++;
}
}
return returnC;
Also we can user String Buffer class to to create another string of duplicate charracters-- and then we can display it--
public static void main(String arghya[])
{
String name="ARGHYA MUKHERJEE";
StringBuffer duplicate=new StringBuffer();
Set<Character> charlist=new HashSet<Character>();
for(int i=0;i<name.length();i++) {
Character c=name.charAt(i);
if(charlist.contains(c))
{
duplicate.append(c);
//System.out.println(c+ " is repeatitive character in the string");
}
else {
charlist.add(c);
}
}
System.out.print("Duplicate characters which has appeared are as follows: "+duplicate);
}

Error to find the first no repeated char in Java

I have a strange issue to print the first non repeated character from String.
If I put for example "sasso" as String it gives me back correctly: 'a'
but if I try with "sassa" I wonder why it gives me back: "s"
public class FirstChar {
public char findFirst(String s) {
boolean[] letters = new boolean[26];
char[] firstLetter = new char[26];
for (int i = 0; i < s.length(); i++) {
if (letters[s.charAt(i) - 97] &&
(firstLetter[0] != (s.charAt(i)))) {
System.out.println( firstLetter[0]);
return firstLetter[0];
}
letters[s.charAt(i) - 97] = true;
char c = (char) (s.charAt(i));
firstLetter[i] = c;
}
System.out.println(firstLetter[1]);
return firstLetter[1];
}
public static void main(String args[]) {
FirstChar obj = new FirstChar();
obj.findFirst("sassa");
}
}
You need to ensure that firstLetter acts as a queue of non repeating characters and remove from it, the moment repeated character is encountered. You are always returning character at 0th or 1st position without overwriting firstLetter array elements.
In case is sassa, when last character a is encountered, conditions in first if evaluate to true and thus return s which is the first character stored in the firstLetter array.
You need HashMap and Queue to achieve this

Desk check fail: Sentences broken up in middle of words and not on spaces

This is part of a whole program. The problem I'm having issues with is when given n, the program is supposed to insert a new line character at the current or last space to make sure that the character count (spaces aren't included in count) never exceeds n before a new line character is inserted. Its current behavior is that it will split words with new line characters. Guarantees: n will never exceed the char count of largest word. Example: n = 9, To be or not to be that is the question. Wanted behavior:
To be or not
to be that
is the
question
Current behavior:
To be or not
to be th
at is
the question
As you can see, the spaces aren't replaced like intended and words are broken by new line character. I've desk-checked multiple times, and I can't seem to find the problem. Please help!
public class Separator {
int n; //the int in front of read line
int cCount = 0; //character counter
int i;
int lastSpc;
char c; //for character iterator
String linePart;
public String separate(int n, String linePart) {
StringBuilder finLine = new StringBuilder(linePart);
for (i = 0; i < linePart.length(); i++) { //inspects each character in string.
c = linePart.charAt(i);
if (c == ' ') { //determines whether or not char is a space
if (cCount == n ) { //checks char count
finLine.replace(i, i, System.lineSeparator()); //adds new line if char count is reached right before space.
cCount = 0;
lastSpc = i;
}
else {
lastSpc = i; //assigns index to variable so no need to reverse through string.
}
}
else {
cCount++;
if (cCount == n) { //if char count is reached while inspecting letter,
finLine.replace(lastSpc, lastSpc, System.lineSeparator()); //replaces last space with new line char
cCount = i - lastSpc;
}
}
}
return finLine.toString();
}
}
Indexes into finLine no longer match indexes into linePart after the first newline is added. Use finLine instead of linePart to keep the indexing consistant.
public String separate(int n, String linePart) {
StringBuilder finLine = new StringBuilder(linePart);
int lines_added = 0;
for (i = 0; i < finLine.length(); i++) { //inspects each character in string.
c = finLine.charAt(i);
if (c == ' ') { //determines whether or not char is a space
if (cCount == n ) { //checks char count
finLine.replace(i, i+1, System.lineSeparator()); //adds new line if char count is reached right before space.
cCount = 0;
lastSpc = i ;
}
else {
lastSpc = i; //assigns index to variable so no need to reverse through string.
}
}
else {
cCount++;
if (cCount == n) { //if char count is reached while inspecting letter,
finLine.replace(lastSpc, lastSpc+1, System.lineSeparator()); //replaces last space with new line char
cCount = i - lastSpc;
}
}
}
return finLine.toString();
}

Returning added string to

I'm trying to return strings in different lines given these conditions. Since I cannot use the += in Java with strings, how do I make one giant string that is spaced per line but "stacks?" In other words, how do I add a new string within a loop to an old string?
/**
Returns a String that concatenates all "offending"
words from text that contain letter; the words are
separated by '\n' characters; the returned string
does not contain duplicate words: each word occurs
only once; there are no punctuation or whitespace
characters in the returned string.
#param letter character to find in text
#return String containing all words with letter
*/
public String allWordsWith(char letter)
{
String result = "";
int i = 0;
while (i < text.length())
{
char newchar = text.charAt(i);
if (newchar == letter)
{
int index1 = text.lastIndexOf("",i);
int index2 = text.indexOf("",i);
String newstring = '\n' + text.substring(index2,index1);
}
i++;
}
return result;
}
Modify the result string, and fix your "word boundary" tests.
if (newchar == letter) {
int index1 = text.lastIndexOf(' ',i);
int index2 = text.indexOf(' ',i);
// TODO -- handle when index1 or index2 is < 0; that means it wasn't found,
// and you should use the string boundary (0 or length()) instead.
String word = text.substring( index2,index1);
result += "\n" + word;
}
If you were really concerned about performance you could use a StringBuilder and append(), but otherwise I strongly favour += for being concise & readable.
you are re-initializing your string in loop every time. Move the string declaration outsid eof loop:
Replace this
String newstring = '\n' + text.substring(index2,index1);
with
result = '\n' + text.substring(index2,index1);
First, use a StringBuilder.
Second, use System.getProperty("line.separator") to ensure proper line breaks are used.
Edited code:
public String allWordsWith(char letter)
{
StringBuilder sb = new StringBuilder();
int i = 0;
while (i < text.length())
{
char newchar = text.charAt(i);
if (newchar == letter)
{
int index1 = text.lastIndexOf("",i);
int index2 = text.indexOf("",i);
sb.Append(text.substring(index2,index1));
sb.Append(System.getProperty("line.separator"));
//I put the new line after the word so you don't get an empty
//line on top, but you can do what you need/want to do in this case.
}
i++;
}
return result;
}
Use StringBuilder as following:
public String allWordsWith(char letter){
//String result = "";
StringBuilder result = new StringBuilder();
int i = 0;
while (i < text.length()){
char newchar = text.charAt(i);
if (newchar == letter){
int index1 = text.lastIndexOf("",i);
int index2 = text.indexOf("",i);
result.append('\n' + text.substring(index2,index1));
}
i++;
}
return result.toString();
}
String text = "I have android code with many different java, bmp and xml files everywhere in my project that I used during the drafting phase of my project.";
String letter = "a";
Set<String> duplicateWordsFilter = new HashSet<String>(Arrays.asList(text.split(" ")));
StringBuilder sb = new StringBuilder(text.length());
for (String word : duplicateWordsFilter) {
if (word.contains(letter)) {
sb.append(word);
sb.append("\n");
}
}
return sb.toString();
result is:
android
have
java,
drafting
and
many
that
phase

Categories

Resources