3

I am very new to Java and as a starter I have been offered to try this at home.

Write a program that will find out number of occurences of a smaller string in a bigger string as a part of it as well as an individual word. For example,

Bigger string = "I AM IN AMSTERDAM", smaller string = "AM".

Output: As part of string: 3, as a part of word: 1.

While I did nail the second part (as a part of word), and even had my go at the first one (searching for the word as a part of the string), I just don't seem to figure out how to crack the first part. It keeps on displaying 1 for me with the example input, where it should be 3.

I have definitely made an error- I'll be really grateful if you could point out the error and rectify it. As a request, I am curious learner- so if possible (at your will)- please provide an explanation as to why so.

import java.util.Scanner;
public class Program {
static Scanner sc = new Scanner(System.in);
static String search,searchstring;
static int n;
void input(){
    System.out.println("What do you want to do?"); System.out.println("1.     
Search as part of string?");
    System.out.println("2. Search as part of word?");
    int n = sc.nextInt();
    System.out.println("Enter the main string"); searchstring = 
sc.nextLine();
    sc.nextLine(); //Clear buffer
    System.out.println("Enter the search string"); search = sc.nextLine();
}
static int asPartOfWord(String main,String search){
    int count = 0; 
    char c; String w = "";
    for (int i = 0; i<main.length();i++){
        c = main.charAt(i);
        if (!(c==' ')){
            w += c;
        }
        else {
            if (w.equals(search)){
                count++;
            }
            w = ""; // Flush old value of w
        }
    }
    return count;
}
static int asPartOfString(String main,String search){
    int count = 0;
    char c; String w = ""; //Stores the word 
    for (int i = 0; i<main.length();i++){
        c = main.charAt(i);
        if (!(c==' ')){
            w += c;
        }
        else {
            if (w.length()==search.length()){
                if (w.equals(search)){
                    count++;
                }
            }
            w = ""; // Replace with new value, no string
        }
    }
    return count;
}
public static void main(String[] args){
    Program a = new Program();
    a.input();
    switch(n){
        case 1: System.out.println("Total occurences: " + 
         asPartOfString(searchstring,search));
        case 2: System.out.println("Total occurences: " +  
         asPartOfWord(searchstring,search));
        default: System.out.println("ERROR: No valid number entered");
    }
  }
}

EDIT: I will be using the loop structure.

Mission Coding
  • 301
  • 4
  • 12
  • Your going to get many answers for this question. If the providers are competent, than they will all be correct. Take the time to learn from all of them. Some will be simple. Some complex. And other will look at the problem very differently to you. – Brett Walker Jun 06 '15 at 15:27
  • Thanks for the advice, @BrettWalker. Will try to soak in the best. – Mission Coding Jun 06 '15 at 15:28
  • Do you want to loop over the string by yourself or would it be ok to use a solution like this one: http://stackoverflow.com/questions/767759/occurrences-of-substring-in-a-string – Pinguin895 Jun 06 '15 at 15:29
  • I'd be using loops. Thanks. – Mission Coding Jun 06 '15 at 15:30

5 Answers5

3

A simpler way would be to use regular expressions (that probably defeats the idea of writing it yourself, although learning regexes is a good idea because they are very powerful: as you can see the core of my code is 4 lines long in the countMatches method).

public static void main(String... args) {
  String bigger = "I AM IN AMSTERDAM";
  String smaller = "AM";

  System.out.println("Output: As part of string: " + countMatches(bigger, smaller) +
          ", as a part of word: " + countMatches(bigger, "\\b" + smaller + "\\b"));
}

private static int countMatches(String in, String regex) {
  Matcher m = Pattern.compile(regex).matcher(in);
  int count = 0;
  while (m.find()) count++;
  return count;
}

How does it work?

  • we create a Matcher that will find a specific pattern in your string, and then iterate to find the next match until there is none left and increment a counter
  • the patterns themselves: "AM" will find any occurrence of AM in the string, in any position. "\\bAM\\b" will only match whole words (\\b is a word delimiter).

That may not be what you were looking for but I thought it'd be interesting to see another approach. An technically, I am using a loop :-)

assylias
  • 321,522
  • 82
  • 660
  • 783
1

Although writing your own code with lots of loops to work things out may execute faster (debatable), it's better to use the JDK if you can, because there's less code to write, less debugging and you can focus on the high-level stuff instead of the low level implementation of character iteration and comparison.

It so happens, the tools you need to solve this already exist, and although using them requires knowledge you don't have, they are elegant to the point of being a single line of code for each method.

Here's how I would solve it:

static int asPartOfString(String main,String search){
    return main.split(search, -1).length - 1;
}

static int asPartOfWord(String main,String search){
    return main.split("\\b" + search + "\\b", -1).length - 1
}

See live demo of this code running with your sample input, which (probably deliberately) contains an edge case (see below).

Performance? Probably a few microseconds - fast enough. But the real benefit is there is so little code that it's completely clear what's going on, and almost nothing to get wrong or that needs debugging.

The stuff you need to know to use this solution:

  • regex term for "word boundary" is \b
  • split() takes a regex as its search term
  • the 2nd parameter of split() controls behaviour at the end of the string: a negative number means "retain blanks at end of split", which handle the edge case of the main string ending with the smaller string. Without the -1, a call to split would throw away the trailing blank in this edge case.
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • Your approach is easiest but don't you think `split(search, -1)` to be replaced with `split(search, 0)`. I get correct results with 0 instead of -1 for both methods. – Rajesh Jun 06 '15 at 16:00
  • @Rajesh `split(search, 0)` fails. `split(search, -1)` succeeds. `0` is the default behaviour, which discards trailing blanks from the array returned. See [javadoc](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#split-java.lang.String-int-) for full description of this behaviour. Using `0` for the sample data returns one less than the correct answer when a match is at the end of the string (note that I changed the code slightly after my initial post - check what code you're running). – Bohemian Jun 06 '15 at 16:39
0

You could use Regular Expressions, try ".*<target string>.*" (Replace target string with what you are searching for.

Have a look at the Java Doc for "Patterns & Regular Expressions"

To search for the occurrences in a string this could be helpful.

Matcher matcher = Pattern.compile(".*AM.*").matcher("I AM IN AMSTERDAM")
int count = 0;

while (matcher.find()) {
    count++;
}
Brett Walker
  • 3,566
  • 1
  • 18
  • 36
0

For the first part of your Exercise this should work:

static int asPartOfWord(String main, String search) {
    int count = 0;
    while(main.length() >= search.length()) { // while String main is at least as long as String search
         if (main.substring(0,search.length()).equals(search)) {  // if String main from index 0 until exclusively search.length() equals the String search, count is incremented;
             count++;
         }
    main = main.substring(1); // String main is shortened by cutting off the first character
    }
    return count;

You may think about the way you name variables:

   static String search,searchstring;
   static int n;

While search and searchstring will tell us what is meant, you should write the first word in lower case, every word that follows should be written with the first letter in upper case. This improves readability.

static int n won't give you much of a clue what it is used for if you read your code again after a few days, you might use something more meaningful here.

  static String search, searchString;
  static int command;
RoboRambo
  • 1
  • 1
  • 4
0

Here's an alternative (and much shorter) way to get it to work using Pattern and Matcher,
or more commonly known as regex.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CountOccurances {

    public static void main(String[] args) {

        String main = "I AM IN AMSTERDAM";
        String search = "AM";

        System.out.printf("As part of string: %d%n",
                asPartOfString(main, search));

        System.out.printf("As part of word: %d%n",
                asPartOfWord(main, search));        
    }

    private static int asPartOfString(String main, String search) {

        Matcher m = Pattern.compile(search).matcher(main);
        int count = 0;
        while (m.find()) {
            count++;
        }
        return count;
    }

    private static int asPartOfWord(String main, String search) {

        // \b - A word boundary
        return asPartOfString(main, "\\b" + search + "\\b");
    }
}

Output:

As part of string: 3
As part of word: 1
almightyGOSU
  • 3,731
  • 6
  • 31
  • 41