Найти уникальный символ в строке? Что не так в логике. Я не могу найти ошибку

#java

#java

Вопрос:

Я пытаюсь найти символы, которые встречаются ровно один раз в строке, где строка содержит только буквы.

Вот мой код:

 public static void main(String[] args)
{
    int count=0;
    Scanner sc= new Scanner(System.in);
    System.out.println("Enter the string: ");
    String s=sc.nextLine();
    char ch1=s.charAt(0);
    if(s.matches("[A-Za-z] "))
    {
        for(int i=0;i<s.length();i  )
        {
            System.out.println(s.length());
            System.out.println(ch1);
            for(int j=1;i<s.length();j  )
            {
                if(s.charAt(i)==s.charAt(j))
                {
                    count  ;
                }               
            }
            if(count == 1)
            {
                System.out.println(s.charAt(i));
            }
                
        }
    }
    else
        System.out.println("Invalid");  
}
  

Я получаю StringIndexOutOfBoundsException .

Что я делаю не так?

Комментарии:

1. Попробуйте инициализировать счетчик внутри первого цикла. Способ, которым вы инициализируете его (один раз) для всей программы, будет продолжать увеличивать значение, даже когда вы переходите к следующему символу.

2. Похоже, вы получите StringIndexOutOfBoundsException . Это то, что вы получаете?

3. Я собираюсь предположить «да»…

4. ошибка, показывающая мне исключение в потоке «main» java.lang. Исключение StringIndexOutOfBoundsException: индекс строки выходит за пределы диапазона: 3

Ответ №1:

Попробуйте инициализировать счетчик внутри первого цикла (или, поскольку он не используется вне цикла, измените объявление). Способ, которым вы инициализируете его (один раз) для всей программы, будет продолжать увеличивать значение, даже когда вы переходите к следующему символу. Кроме того, поскольку вы тестируете с использованием 1, измените инициализацию внутреннего цикла на 0 и условие на j (в настоящее время это i)

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

public class UniqueChar
{
    public static void main(String[] args)
    {
        Scanner sc = new Scanner(System.in);
        System.out.println("Enter the string: ");
        String s = sc.nextLine().toLowerCase(); //good idea to change casing
        char ch1 = s.charAt(0);

        if (s.matches("[A-Za-z] "))
        {
            for (int i = 0; i < s.length(); i  )
            {
                int count = 0; // initialize here
                System.out.println(s.length());
                System.out.println(ch1);

               for (int j = 0; j < s.length(); j  ) // changed initialization to 0 and condition to j
                {
                    if (s.charAt(i) == s.charAt(j))
                    {
                        count  ;
                    }               
                }
                if (count == 1)
                {
                    System.out.println(s.charAt(i));
                }
                    
            }
        } else {
            System.out.println("Invalid");  
        }
    }
}
  

Кроме того, было бы неплохо изменить входные данные на единообразный регистр. Текущая настройка будет рассматривать A и a как разные, если только это не является требованием (в этом случае вы должны игнорировать нижний регистр).

Ответ №2:

Вы забываете вернуть вашей переменной count значение 0 во внешнем цикле после проверки s [i]-го символа.

 for(int i = 0; i < s.length(); i  )
{
    System.out.println(s.length());
    System.out.println(ch1);

    count = 0; //missing line

    for(int j = 1; j < s.length(); j  )
    {
        if(s.charAt(i) == s.charAt(j))
        {
            count  ;
        }               
    }
    if(count == 1)
    {
        System.out.println(s.charAt(i));
    }
        
}
  

Без этого он сохранял бы значение count из ранее проверенного символа и поэтому не начинался бы с нуля (что нам и нужно сделать, чтобы проверить частоту для каждого символа).

Комментарии:

1. Я думаю, вы допустили ошибку в своем внутреннем forloop for(int j = 1; i < s.length(); j ) это должно быть for(int j = 1; j < s.length(); j )

2. Спасибо, Вивек, исправил это, это было в вопросе, поэтому не потрудился проверить это

Ответ №3:

Помимо установки count = 0 перед началом каждого внутреннего цикла (или даже лучше — перемещения объявления внутри цикла) и исправления вашей опечатки с i < s.length() на j < s.length() во внутреннем цикле, вот предложение:

Если вы ожидаете огромные строки, вы могли бы реализовать O(n) решение, повторив строку только один раз и подсчитав, сколько раз встречался каждый символ, и, наконец, проверив, какой из них произошел один раз.

Ответ №4:

Эта задача может быть реализована с использованием Java Stream API:

  • вычислите частоты каждого символа во входной строке, соберите их в map
  • записи карты фильтрации, где частота равна 1 (уникальный символ)

Пример:

 public static void printUnique(String str) {
    str.chars()                            // get IntStream of characters
       .mapToObj(x -> (char) x)            // convert to stream of Character
       .collect(Collectors.groupingBy(     // count frequencies using LinkedHashMap to prevent order of insertion
           x -> x, LinkedHashMap::new, Collectors.counting())) 
       .entrySet().stream()                // get stream of entries
       .filter(e -> e.getValue() == 1)     // detect uniques
       .map(Map.Entry::getKey)             // remap Map.Entry<Character, Long> to character
       .forEach(System.out::print);        // print in a row
    System.out.println();
}
  

Тест:

 printUnique("Hello World");
printUnique("quickbrownfoxjumpsoverthelazydog");
  

Выходной сигнал:

 He Wrd
qickbwnfxjmpsvthlazydg