-2

So, I need to write a program using loops that takes a string and counts what and how many letters appear in that string. (string "better butter" would print "b appears 2 times, e appears 3 times, ' '(space) appears 1 time, and so on). While I understand the idea and concept behind this assignment, actually pulling it off has been rough.

My nested for loop is where the problems are coming from, I assume. What I've written only loops once (i think) and just shows the first character and says there's only one of that character.

Edit: Preferably without using Map or arrays. I'm fine with using them if it's the only way, but they've not been covered in my class so I'm trying to avoid them. Every other similar question to this (that I've found) uses Map or array.

import java.util.Scanner;

class myString{
    String s;

    myString() {
        s = "";
    }
    void setMyString(String s) {
        this.s = s;
    }
    String getMyString() {
        return s;
    }
    String countChar(String s){
        s = s.toUpperCase();
        int cnt = 0;
        char c = s.charAt(cnt);
        for (int i = 0; i <= s.length(); i++)
            for (int j = 0; j <= s.length(); j++)  //problem child here
                c = s.charAt(cnt);
                cnt++;
                if (cnt == 1)
                    System.out.println(c+" appears "+cnt+" time in "+s);
                else
                    System.out.println(c+" appears "+cnt+" times in "+s);
                return "for";  //this is here to prevent complaint from the below end bracket.
    }
}

public class RepeatedCharacters {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String s;

        System.out.println("Enter a sentence: ");
        s = in.nextLine();


        myString myS = new myString();
     //   System.out.println(myS.getMyString());
     //   System.out.println(myS.countChar());
        myS.countChar(s);
                }

}
Conf3tti
  • 29
  • 4
  • Have you tried debugging? – shmosel Oct 20 '16 at 22:48
  • Your try to do the counting and the printing of the results at the same time. Separate those two issues. First count characters and then after that output the information. – NineBerry Oct 20 '16 at 22:51
  • Possible duplicate of [count occurance of element in array](http://stackoverflow.com/questions/13923442/count-occurance-of-element-in-array) – Enfyve Oct 20 '16 at 22:58
  • You need to use brackets around your `for` and `if` statements. The indentation doesn't mean they are actually inside the loop. – 4castle Oct 20 '16 at 23:14

6 Answers6

1

First you will need to scan the entire string and store the counts of each characters. Later you can just print the counts.

Algorithm 1:

  1. Use a HashMap to store the character as key and its count as value. (If you are new to Java, you might want to read up on HashMaps.)

  2. Every time you read a character in your for loop, check if it present in the HashMap. If yes, then increment the count by 1. Else add a new characters to the map with count 1.

Printing: Just iterate on your HashMap and print out the character and their respective counts.

Issue with your code: You are trying to print the count as soon as you read a character. But the character might appear again later in the string. So you need to keep track of the characters you have already read.

Algorithm 2:

String countChar(String s){
    has_processed = []
    for i = 0 to n
        cnt = 0
        if s.charAt(i) has been processed
            continue;
        for j = i+1 to n
            if (s.charAt(i) == s.charAt(j))
                cnt++
                add s.charAt(i) to has_processed array
        print the count of s.charAt(i)
}
Renuka Deshmukh
  • 998
  • 9
  • 16
  • Is there a way without using HashMap or array? I apologize, I forgot to add it into the original question. – Conf3tti Oct 20 '16 at 23:05
  • yes you can do it without using HashMap or Arrays. I will update my solution. – Renuka Deshmukh Oct 20 '16 at 23:07
  • Doing it without an array takes longer. – Charles Oct 20 '16 at 23:13
  • Yeah. Hence, my solution to use HashMap! – Renuka Deshmukh Oct 20 '16 at 23:15
  • @RenukaDeshmukh that's what I'm saying. Either your solution or my solution should be used, depending on what characters OP intends to handle and his comfort level with java containers. – Charles Oct 20 '16 at 23:16
  • @c650 agreed. I was assuming the OP has particular requirements to not use arrays and maps as part course work. – Renuka Deshmukh Oct 20 '16 at 23:20
  • Doing this without some sort of collection would be costly and tedious, as you would likely end up counting the same character again and again later on in the array. You'd need an array just to look that up. – Charles Oct 20 '16 at 23:22
  • yes. though slightly more space efficient, this approach has a bad time complexity. – Renuka Deshmukh Oct 20 '16 at 23:24
  • Your Algorithm 2 is not more space efficient, as it has a worst-case space consumption of 256 due to `has_processed`. And all of the resizings of that array are also costly. A HashMap or Array, thus, is simply the only good way to do this. – Charles Oct 20 '16 at 23:26
  • c650. I agree with you. I personally would go with HashMap. I just added the second algorithm as the OP asked for it. I agree it has a bad runtime. – Renuka Deshmukh Oct 20 '16 at 23:29
  • @c650 : The reason I asked for a method without HashMap or array is because this is a homework problem. Neither of those two algorithms have been covered (to my knowledge), therefore I requested a solution without. I recognize that it is horribly inefficient, but I find it important to first understand how to work the problem without using functions to make the code faster. – Conf3tti Oct 20 '16 at 23:42
  • @Conf3tti if you don't use any collection at all, you're going to get output like: "b is 5 times" ... "b is 4 times" ... I don't think you'd want this behaviour. – Charles Oct 20 '16 at 23:45
  • @c650 I just thought of a simple way to avoid this behaviour. Every time you count a character, replace it with '#' or '$' or some other wild character. Next time you encounter this variable, you can simply skip it. – Renuka Deshmukh Oct 26 '16 at 19:13
  • @RenukaDeshmukh that isn't a bad idea! Of course, such only works with charset constraints in place... – Charles Oct 26 '16 at 21:39
  • @RenukaDeshmukh although String's are immutable in Java so you can't just change a character. – Charles Oct 27 '16 at 00:50
  • That is very true!! – Renuka Deshmukh Oct 27 '16 at 05:24
1

Use a frequency array to get an answer in linear time.

/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        String s = "better butter";

        int freq[] = new int[26];

        int i;
        for (i = 0; i < s.length(); i++) {
            if (s.charAt(i) >= 'a' && s.charAt(i) <= 'z')
                freq[s.charAt(i)-'a']++;
        }

        for (i = 0; i < freq.length; i++) {
            if (freq[i] == 0) continue;
            System.out.println((char)(i+'a') + " appears " + freq[i] + " times" );
        }
    }
}

Ideone Link

Note that this can be expanded to include uppercase letters, but for demonstrative purposes, only lowercase letters are handled in the above code.

EDIT: While the OP did ask if it was possible to do this without an array, I would recommend against such. That solution would have terrible time complexity and repeat character counts (unless an array is used to keep track of seen characters, which is counter to the aim). Thus, the above solution is the best way to do it in a reasonable amount of time (linear) with limited space consumption.

Charles
  • 1,384
  • 11
  • 18
0

I would do the following. Create a HashMap which keeps track of which unique characters are in the string and the count for each character.

You only need to iterate over the string once, and put each character into the HashMap. if the characer is in the map, icrement the integer count in the map, else add 1 to the map for that character. Print out the map with toString() to get the result. The whole thing can be done in about 4 lines of code.

adamM
  • 1,116
  • 10
  • 29
  • Is there a way without using HashMap or array? I apologize, I forgot to add it into the original question. – Conf3tti Oct 20 '16 at 23:04
  • You restated what Renuka said. Why? – Charles Oct 20 '16 at 23:18
  • If you look at the timestamps, you will see that I really did not restate what Renuka said, but Renuka is simply a faster typer. I came in few minutes after the other post. – adamM Oct 20 '16 at 23:38
  • @Conf3tti you can do it pretty easily without and array, but it would require at least two iterations (loops) over the string. – adamM Oct 20 '16 at 23:40
0

The only thing being done in your nested for loop with the following

c = s.charAt(cnt)

is setting the c char to the value of the first letter (i.e. index 0 of the string) over and over and over until you've looped through the string n^2 times. In other words, you're not incrementing your cnt counter within the for loops at all.

bradykey
  • 443
  • 2
  • 7
0

Here is my version of countChar(String s)

boolean countChar(String s) {
    if(s==null) return false;
    s = s.toUpperCase();
    //view[x] will means that the characted in position x has been just read
    boolean[] view = new boolean[s.length()];

    /*
    The main idea is:
    foreach character c = s.charAt(x) in the string s, I have a boolean value view[x] which say if I have already examinated c.
    If c has not been examinated yet, I search for other characters equals to c in the rest of the string.
    When I found other characters equals to c, I mark it as view and I increment count with count++.
    */
    for (int i = 0; i < s.length(); i++) {
        if (!view[i]) {
            char tmp = s.charAt(i);
            int count = 0;
            for (int j = i; j < s.length(); j++) {
                if (!view[j] && s.charAt(j) == tmp) {
                    count++;
                    view[j] = true;
                }
            }
            System.out.println("There were " + count + " " + tmp);
        }
    }
    return true;
}

It should work, excuse me for my English because I'm italian

Luca Di Liello
  • 1,486
  • 2
  • 17
  • 34
  • The `for (boolean a : view)` loop is unnecessary for two reasons: 1) boolean arrays are initialized to `false` by default, and 2) `a` is a copy and won't modify the actual element in the array. – Charles Oct 20 '16 at 23:39
  • Your are right and I didn't know that array of booleans were initialized to false. – Luca Di Liello Oct 21 '16 at 10:23
0

Suggestion: try to use meaningful names for your variables; it will help you a lot in your career. Also class names should always start with a capital letter.

Although it is not the quickest solution in terms of performance, the most simple solution should be:

import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
int count = freq.containsKey(word) ? freq.get(word) : 0;
freq.put(word, count + 1);

Source: Most efficient way to increment a Map value in Java

Please next time use the search function before posting a new question.

Community
  • 1
  • 1