Исправлена проблема с циклическим удалением букв в строке

#java #string #loops

#Ява #строка #петли

Вопрос:

Поэтому я создаю программу, которая удаляет дубликаты букв в строке. Последним шагом этого является обновление старой строки до новой строки и повторение цикла по новой строке. Я верю, что все работает, кроме зацикливания на новой струнной части. Есть какие-нибудь идеи, из-за чего это может не сработать? Он будет работать так, как задумано, в течение одного прохода, а затем после этого он не будет проходить через новый цикл

 public class homework20_5 {  public static void main(String[] arg) {  Scanner scanner = new Scanner(System.in);  String kb = scanner.nextLine();  int i;  for (i = 0; i lt; kb.length(); i  ) {  char temp = kb.charAt(i);  if(temp == kb.charAt(i 1)) {  kb = kb.replace("" temp, "");  i = kb.length()   i;  }  }  System.out.println(kb);  } }  

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

1. «не пройду через новую петлю»… вы имеете в виду, когда нажимаете enter более одного раза?

2. Это означает, что он зацикливается на дубликатах только один раз. Это не продолжается до тех пор, пока не исчезнут дубликаты.

3. Что вы считаете дубликатом? Вы хотите, чтобы у вас остались все отдельные буквы или вы хотите свернуть последовательности одних и тех же букв? Например, «AABBCCABC» вы хотите «ABC» или «ABCABC»?

4. XYYYXAC затем станет XXAC, который затем станет AC, поэтому первый приведенный вами пример.

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

Ответ №1:

Вместо использования сложных алгоритмов и подобных циклов вы можете просто использовать HashSet , которые будут работать так же, как список, но не позволят дублировать элементы.

 private static String removeDuplicateWords(String str) {   HashSetlt;Charactergt; xChars = new LinkedHashSetlt;gt;();  for(char c: str.toCharArray()) {  xChars.add(c);  }   StringBuilder sb = new StringBuilder();  for (char c: xChars) {  sb.append(c);  }   return sb.toString();  }  

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

1. Может removeDuplicateLetters быть (не словами) вместо этого? Кроме того, я не уверен, что он делает то, что хочет ОП. Это позволит букве появляться только один раз, а не сворачивать последовательности одной и той же буквы.

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

Ответ №2:

Таким образом, вы действительно хотите полностью удалить все вхождения, которые появляются более одного раза, а не только дубликаты (сохраняя при этом один экземпляр)?

«Да, это совершенно верно «

В этом случае ваша идея не сократит ее, потому что ваше обнаружение дубликатов писем может обнаруживать только непрерывные последовательности дубликатов. Очень простым способом было бы использовать 2 набора для идентификации уникальных букв за один проход.

  public class RemoveLettersSeenMultipleTimes {   public static void main(String []args){  String input = "abcabdgag";    Setlt;Charactergt; lettersSeenOnce = lettersSeenOnceIn(input);    StringBuilder output = new StringBuilder();     for (Character c : lettersSeenOnce) {  output.append(c);  }    System.out.println(output);    }    private static Setlt;Charactergt; lettersSeenOnceIn(String input) {  Setlt;Charactergt; seenOnce = new LinkedHashSetlt;gt;();  Setlt;Charactergt; seenMany = new HashSetlt;gt;();    for (Character c : input.toCharArray()) {  if (seenOnce.contains(c)) {  seenMany.add(c);  seenOnce.remove(c);  continue;  }    if (!seenMany.contains(c)) {  seenOnce.add(c);  }  }    return seenOnce;  }  }   

Ответ №3:

Здесь есть несколько проблем:

Проблема 1

 for (i = 0; i lt; kb.length(); i  ) {  

должно быть

 for (i = 0; i lt; kb.length() - 1; i  ) {  

Потому что это

 if (temp == kb.charAt(i 1))  

взорвется с ArrayIndexOutOfBoundsException помощью другого.

Проблема 2

Удалите эту строку:

 i = kb.length()   i;  

Я не понимаю, в чем там намерение, но, тем не менее, оно должно быть удалено.

Проблема 3

Вместо большого количества кода есть однострочное решение:

 String deduped = kb.replaceAll("["   input.replaceAll("(.)(?=.*\1)|.", "$1")   "]", "");  

Это работает за счет:

  • поиск всех символов dupe с помощью input.replaceAll("(.)(?=.*\1)|.", "$1") , что, в свою очередь, работает, потребляя каждый символ, либо записывая его в группу 1, если у него есть dupe, либо просто потребляя его, если он не является dupe
  • создание класса символов регулярного выражения из dupes, который используется для удаления их всех (заменить пробелом)

Ответ №4:

Допустим, вы вводите в программу ввод «AAABBC», тогда ожидаемый результат должен быть «ABC».

Теперь в цикле for i увеличивается с 0 до 5.

После 1-й итерации: kb становится AABBC и i становится 5 0 = 5 и увеличивается до 6. И теперь условие для цикла for-это то i lt; kb.length() , что приравнивается к 6 lt; 5 возвращению false. Следовательно, цикл for заканчивается всего после одной итерации.

Таким образом, проблемная строка кода i = kb.length() i; , а также условие цикла постоянно меняются по мере изменения размера kb .

Я бы предложил использовать цикл while, как в следующем примере, если вы не слишком беспокоитесь об эффективности.

 public static void main(String[] arg) {  String kb = "XYYYXAC";  int i = 0;   while (i lt; kb.length()) {  char temp = kb.charAt(i);  for (int j = i   1; j lt; kb.length(); j  ) {  char dup = kb.charAt(j);  if (temp == dup) {  kb = removeCharByIndex(kb, j);  j--;  }  }  i  ;  }  System.out.println(kb); }  private static String removeCharByIndex(String str, int index) {  return new StringBuilder(str).deleteCharAt(index).toString(); }  

Выход: XYAC

ПРАВКА: Я неправильно понял ваши требования. Итак, глядя на приведенные выше комментарии, вы хотите, чтобы все дубликаты и целевой персонаж были удалены. Таким образом, приведенный выше код может быть изменен следующим образом.

 public static void main(String[] arg) {  String kb = "XYYYXAC";  int i = 0;   while (i lt; kb.length()) {  char temp = kb.charAt(i);  boolean hasDup = false;  for (int j = i   1; j lt; kb.length(); j  ) {  if (temp == kb.charAt(j)) {  hasDup = true;  kb = removeCharByIndex(kb, j);  j--;  }  }  if (hasDup) {  kb = removeCharByIndex(kb, i);  i--;  }    i  ;  }  System.out.println(kb); }  private static String removeCharByIndex(String str, int index) {  return new StringBuilder(str).deleteCharAt(index).toString(); }  

Выход: ПЕРЕМЕННЫЙ ток

Хотя это не лучшее и определенно не эффективное решение этой проблемы, я думаю, что вы можете получить идею повторения входной строки символ за символом и удаления ее, если в ней есть дубликаты.

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

1. я знаю, что i = kb.length() i; это проблемная часть, но я не знаю, что с этим делать. Я перепробовал так много решений, но, похоже, ничего не работает.

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

Ответ №5:

Попробуй:

 for (i = 0; i lt; kb.length(); i  ) {  for (int j = i   1; j lt; kb.length(); j  ) {  char temp = kb.charAt(j);  // duplicate  if (temp == kb.charAt(i)) {  // remove all duplicate chars  kb = kb.replaceAll(String.valueOf(temp), "");  i--;  break;  }  }  }  

Ответ №6:

Следующий ответ касается только преобразования XYYYXACX в ACX . Если бы мы хотели иметь AC , это был бы совершенно другой ответ. Другие ответы уже говорят об этом, и я приглашу вас также ознакомиться с contains методом String .

Нам следует подумать о том, чтобы избегать-в большинстве случаев — изменения того, что мы повторяем. Использование временной переменной может быть своего рода решением. Чтобы использовать его, мы могли бы изменить наше мышление. Вместо того, чтобы стирать нежелательные буквы, мы можем сохранить те, которые хотим.

Чтобы определить нужный символ, нам нужно проверить, все ли окружающие буквы отличаются от проверяемого. Это будет прямо противоположно тому, что вы сделали с if(temp == kb.charAt(i 1)) { лайком if(temp != kb.charAt(i 1)) { . Но учитывая, что тестируемая строка больше не изменится, нам нужно будет протестировать и предыдущую букву как if(temp != kb.charAt(i-1) amp;amp; temp != kb.charAt(i 1)) { .

Как уже было сказано ранее, как только мы определим букву, мы сохраним значение с временной переменной. Это приведет к замене kb = kb.replace("" temp, ""); на buffer = buffer temp; if buffer -это наша временная переменная, инициализированная пустой строкой (она же. String buffer = ""; ). В конце концов, мы могли бы заменить наше базовое значение временным.

На этом этапе у нас будет:

 public static void main(String[] arg) {  Scanner scanner = new Scanner(System.in);  String kb = scanner.nextLine();  String buffer = "";  int i;  for (i = 1; i lt; kb.length(); i  ) {  char temp = kb.charAt(i);  if(temp != kb.charAt(i-1) amp;amp; temp != kb.charAt(i 1)) {  buffer = buffer   temp;  }  }  kb = buffer;  System.out.println(kb); }  

К сожалению, это не сработает, если вы попытаетесь получить доступ к недопустимым индексам нашей строки. Мы должны рассмотреть два конкретных поведения для первой и последней буквы, потому что они близки только к одной букве. Для этих писем у нас будет только одно сравнение. Таким образом, мы можем сделать их внутри или снаружи петли. Для ясности мы сделаем это снаружи. Для первого это будет выглядеть так же, как if (kb.charAt(0) != kb.charAt(1)) { и if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) { для последнего. Тело условия останется таким же, как и в цикле. После этого мы уменьшим область нашего цикла, чтобы исключить эти символы for (i = 1; i lt; (kb.length() - 1); i ) { .

Теперь у нас будет что-то работающее, но только для одной итерации:

 public static void main(String[] arg) {  Scanner scanner = new Scanner(System.in);  String kb = scanner.nextLine();  String buffer = "";  int i;  if (kb.charAt(0) != kb.charAt(1)) {  buffer = buffer   kb.charAt(0);  }  for (i = 1; i lt; (kb.length() - 1); i  ) {  char temp = kb.charAt(i);  if(temp != kb.charAt(i-1) amp;amp; temp != kb.charAt(i 1)) {  buffer = buffer   temp;  }  }  if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {  buffer = buffer   kb.charAt(kb.length() - 1);  }  kb = buffer;  System.out.println(kb); }  

XYYYXACX станет XXACX .

Как уже было сказано, наша проблема с индексом может возникнуть снова, если строка содержит только одну букву. Однако все это было бы бесполезно, потому что, очевидно, в этой ситуации у нас не может быть дубликата письма. На самом деле, мы должны завернуть все это, чтобы убедиться, что у нас есть по крайней мере две буквы:

 public static void main(String[] arg) {  Scanner scanner = new Scanner(System.in);  String kb = scanner.nextLine();  if (kb.length() gt;= 2) {  String buffer = "";  int i;  if (kb.charAt(0) != kb.charAt(1)) {  buffer = buffer   kb.charAt(0);  }  for (i = 1; i lt; (kb.length() - 1); i  ) {  char temp = kb.charAt(i);  if (temp != kb.charAt(i - 1) amp;amp; temp != kb.charAt(i   1)) {  buffer = buffer   temp;  }  }  if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {  buffer = buffer   kb.charAt(kb.length() - 1);  }  kb = buffer;  }  System.out.println(kb); }  

Последнее, что нужно сделать, это выполнить это лечение до тех пор, пока у нас больше не будет нежелательных писем. Для этой задачи, do { ... } while ( ... ) кажется, идеально подходит. Мы можем использовать для сравнения условий размер строки. Потому что, когда размер предыдущей итерации будет равен временной переменной, мы будем знать, что закончили. Нам нужно будет выполнить это сравнение, прежде чем изменять значение нашей временной переменной на базовое. В противном случае, это всегда будет одно и то же.

В конце концов, потенциальным решением должно быть следующее:

 public static void main(String[] arg) {  Scanner scanner = new Scanner(System.in);  String kb = scanner.nextLine();  Boolean modified;  do {  modified = false;  if (kb.length() gt;= 2) {  String buffer = "";  int i;  if (kb.charAt(0) != kb.charAt(1)) {  buffer = buffer   kb.charAt(0);  }  for (i = 1; i lt; (kb.length() - 1); i  ) {  char temp = kb.charAt(i);  if (temp != kb.charAt(i - 1) amp;amp; temp != kb.charAt(i   1)) {  buffer = buffer   temp;  }  }  if (kb.charAt(kb.length() - 1) != kb.charAt(kb.length() - 2)) {  buffer = buffer   kb.charAt(kb.length() - 1);  }  modified = (kb.length() != buffer.length());  kb = buffer;  }  } while (modified);  System.out.println(kb); }  

Take note that this code is ugly for the sole purpose of the explanation. We should refactor this code. We can improve it a lot for the sake of brevity and, why not, performance.