Problema com String

28 respostas
N

Boas tou aqui com um problema, é o seguinte o objectivo é identificar numa frase as letras que aparecem e as que nao aparecem, e guardar numa string as que aparecem e o mesmo para as que nao aparecem na frase, eu ja consegui identificar isso mesmo so que o problema é que no output as letras estao repetidas, quando eu so quero uma letra, ta aqui o codigo que fiz:

public class ex7 {
	public static void main(String[]args){
		Scanner kbd=new Scanner (System.in);
		
		String S=kbd.nextLine();
		String K=S.toUpperCase();
		String Abc="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		String A="";
		String N="";
		
		for(int i=0;i<=25;i++){
			
			for(int j=0;j<K.length();j++){
			
			if(Abc.charAt(i)==K.charAt(j))
				A=A+K.charAt(j);
				N=N+" ";
				
			
			
			if(Abc.charAt(i)!=K.charAt(j)){
				A=A+" ";				
                                N=N+Abc.charAt(i);
			}
			
			}
			
		}
		
		    System.out.println("Aparecem: "+A);
		System.out.println("Nao aparecem: "+N);	
	}

no output aparece: Aparecem: EEFIX
Nao aparecem: AAAAABBBBBCCCCC...etc
alguem me pode dar uma ideia para nao aparecerem letras repetidas?
Cumps

28 Respostas

ganondorfan

Você pode criar um Set de Character e colecionar todas as letras que contains no seu alfabeto e depois concatenar o resultado na String A (Se o alfabeto tiver completo é só colecionar eles no SET).

As que não aparecem você compara com seu SET(o alfabeto) e gera um vetor ou uma coleção das que são diferentes, o resultado você armazena na sua String N.

Eder_Peixoto

Teu método entra em loop infinito. Veja a linha 19 (j sempre recebe 0).

F

eu realmente nao intendi a pergunta.

é o seguinte o objectivo é identificar numa frase as letras que aparecem e as que nao aparecem, e guardar numa string as que aparecem e o mesmo para as que nao aparecem na frase, eu ja consegui identificar isso

como assim as letras que aparecem?

AlexandreGama

Cara, seu código dificilmente vai rodar.
Você atribui j = 0 no seu for, ou seja, infinito.

Até mais!

AlexandreGama

Nosso amigo Eder Peixoto já tinha dado a dica! :slight_smile:

Até mais!

Eder_Peixoto

Teu código possui também erro de lógica, pois a iteração pela variável Abc deveria ser dentro do for que itera pela String digitada pelo usuário.

Deveria ser algo como:

for( char chAlvo : S ){
    for( char chAlfa : Abc  ){
    }
}
AlexandreGama

Só pra facilitar um pouco, fiz um código semelhante ao seu, e segui a linha dos métodos
utilizados.

public class ProcurandoStrings {
	
	public static void main(String[] args) {
		
	    Scanner kbd = new Scanner(System.in);  
	    String S = kbd.nextLine();  
	    String K = S.toUpperCase();  
	    String Abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";  
	    String A = "";  
	    String N = "";  

	    for (int i = 0; i <= 25; i++) {
	    	if (K.contains(String.valueOf(Abc.charAt(i)))) {
	    		A = A + Abc.charAt(i);
	    	}
	    	else {
	    		N = N + Abc.charAt(i);
	    	}
		}
	    
	    System.out.println("Aparecem: "+A);  
	    System.out.println("Nao aparecem: "+N);   
	}
}

Eu faria de outras formas, mas essa segue mais ou menos o que você estava pensando, assim você
pode implementar da forma que precisa.
E tente implementar de outras formas, tem formas mais elegantes! :wink:

Abraços!

Eder_Peixoto

Uma dica: o tipo das variáveis A e N não deveria ser String, pois a todo momento estão sendo alteradas. O mais aconselhável é utilizar StringBuilder ou StringBuffer.

String: utilize preferencialmente quando o conteúdo não for alterado;

StringBuilder: indicado para utilizar quando o String sofre alterações e quando NÃO  concorrência quanto a acesso (multi-thread);

StringBuffer: indicado para utilizar quando o String sofre alterações e quando  concorrência quanto a acesso (multi-thread);
Eder_Peixoto

[quote=Eder Peixoto]Uma dica: o tipo das variáveis A e N não deveria ser String, pois a todo momento estão sendo alteradas. O mais aconselhável é utilizar StringBuilder ou StringBuffer.

String: utilize preferencialmente quando o conteúdo não for alterado, pois foi implementado para otimizar acesso de leitura;

StringBuilder: indicado para utilizar quando o String sofre alterações e quando NÃO  concorrência quanto a acesso (multi-thread);

StringBuffer: indicado para utilizar quando o String sofre alterações e quando  concorrência quanto a acesso (multi-thread);
AlexandreGama

Exatamente como o Eder Peixoto falou!
Na verdade nasomet reescrevi seu código, como eu disse, seguindo suas variáveis e métodos,
assim mantive alguma “originalidade” do seu código.

Mas como eu disse, Eu faria de outras formas e uma delas é utilizando as dicas que o Eder mencionou.
E como eu disse nasomet, tente implementar de outras formas, tanto seguindo o que o Eder
mencionou, como melhorando outras partes do código.

Assim você se acostumará a codificar da forma correta, não somente da forma que funciona.
E se tiver problemas com novas implementações, é só postar a dúvida.

Só lembrando que não resisti e tirei a comparação de Strings com “==” e utilizei contains.

Abraços!

N

desculpem se não fui muito claro, vou ter em conta as dicas do Eder Peixoto e do AlexandreGama, muito obrigado!
Abraços

L

Bem parecido com o seu e o colega Alexandre, só que nesse exemplo a String com caracteres do alfabeto é percorrida para verificar se o mesmo está no texto digitado, assim o algoritmo fica mais eficiente para textos maiores e os caracteres já estarão ordenados no final.

Como foi dito é aconselhável usar um StringBuider para concatenar os caracteres. Observe que o texto digitado é convertido para maiúsculo.

String str = new Scanner(System.in).nextLine().toUpperCase(); String strAll = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; String strContains = ""; String strNotContains = ""; char[] strChars = strAll.toCharArray(); for (char c : strChars) { boolean contains = str.indexOf(c) != -1; if ((contains) && (strContains.indexOf(c) == -1)) { strContains += c; } else if (strNotContains.indexOf(c) == -1) { strNotContains += c; } } System.out.println("Caracteres que estao no texto : " + strContains); System.out.println("Caracteres que nao estao no texto: " + strNotContains);

AlexandreGama

Olá nasomet
O amigo lsjunior também postou uma implementação, dessa vez usando até foreach.

Agora só um detalhe
O lsjunior indicou que o algoritmo seria mais rápido, mas há um detalhe (tosco neste caso) também.

Imagine a situação:

"ABCDEFGHIJLMNOPQRSTUVWXYZ" //25 Letras
"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ" //50 letras

Repare que ao começar com o alfabeto temos:
para A = 50 comparações sem sucesso
para B = 50 comparações sem sucesso
para C = 50 comparações sem sucesso
.
.
.
para Z = 50 comparação com 1 sucesso
Ou seja, 50x25

Repare que ao começar com o texto temos:
para Z = 25 comparações com 1 sucesso
para Z = 25 comparações com 1 sucesso
.
.
.
para Z = 25 comparações com 1 sucesso
Ou seja, 25x50

Há formas interessantes de se lidar com eficiência de algoritmos e é bacana estudar.
Você terá casos que um algoritmo é eficiente em certos momentos, mas dependendo dos dados, ineficiente ou
igual, como a situação acima.

Abraços!

L

Vc não entendeu bem o algortimo, cada letra do alfateto e verificada uma única vez. Independete do tamanho do texto serão feita apenas as verificações se o texto possui cada uma das 26 letras do alfabeto.

String str = new Scanner(System.in).nextLine().toUpperCase();
String strAll = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String strContains = "";
String strNotContains = "";
// Coloco o alfabeto em caracteres
char[] strChars = strAll.toCharArray();
// Percorro o array com os caracteres do alfabeto
for (char c : strChars) {
    // Verifico se o caracter atual existe no texto
    boolean contains = str.indexOf(c) != -1; // O texto digitado possui o caracter atual
    // Se o texto possuir o caracter atual e o mesmo ainda nao tiver na String strContains
    if ((contains) && (strContains.indexOf(c) == -1)) {
        strContains += c;
    }
    // Se o NAO texto possuir o caracter atual e o mesmo ainda nao tiver na String strNotContains
    else if (strNotContains.indexOf(c) == -1) {
        strNotContains += c;
    }
}

System.out.println("Caracteres que estao no texto    : " + strContains);
System.out.println("Caracteres que nao estao no texto: " + strNotContains);
AlexandreGama

Olá lsjunior! Na verdade não falei em relação ao seu código. :wink:
Falei de modo geral, quando percorremos duas Strings para comparar caracter a caracter.

Se temos a frase “ABC”
e um texto “HHHHHHHHHH”

Não importa se você comparar, nesta situação, da frase para o texto, ou do texto para a frase,
o número de comparações será a mesma.

Primeira situação : 3x10
Segunda situação: 10x3

Quis pegar um “gancho” para a eficiência de algoritmos, apesar do exemplo ser bem, mas bem pobrezinho!

Abraços!

ViniGodoy

Que tal assim?

public class TesteAlfabeto {
    public static void main(String args[]) {
        boolean alfabeto[] = new boolean[25];
        String texto = "Este e um exemplo de teste.";

        texto = texto.toUpperCase();
        for (char ch : texto.toCharArray()) {
            int letra = ch - 65;
            if (letra >= 0 && letra < alfabeto.length)
                alfabeto[letra] = true;
        }

        for (int i = 0; i < 25; i++) {
            System.out.println("A letra " + (char)(i + 65) + " " + (alfabeto[i] ? "está" : "não está") + " no texto.");
        }
    }
}

Esse sim, tem uma performance ótima. Percorre cada caractere do texto apenas uma vez, e não testa contains no alfabeto nenhuma vez.

E pode ser facilmente modificado para não só dizer se a letra existe, mas quantas vezes aparece no texto.

ViniGodoy
Aqui vai um exemplo com a modificação da contagem:
public class TesteAlfabeto {
    public static void main(String args[]) {
        int alfabeto[] = new int[25];
        String texto = "Este e um exemplo de teste.";

        texto = texto.toUpperCase();
        for (char ch : texto.toCharArray()) {
            int letra = ch - 65;
            if (letra >= 0 && letra < alfabeto.length)
                alfabeto[letra]++;
        }

        for (int i = 0; i < 25; i++)
            if (alfabeto[i] != 0)
                System.out.println("A letra " + (char)(i + 65) + " aparece " + alfabeto[i] + " vezes no texto.");
    }
}
AlexandreGama

Boa ViniGodoy!

O seu exemplo é o clássico exemplo que eu fazia em C na faculdade quando o professor
dava as aulas sobre eficiência de algortimos!

O nosso amigo já tem bastante coisa pra se divertir!

Atémais!

L

Ai que ta, nesse exemplo nao ha necessidade de comparacao de caracter por caracter, por que nao quero saber quantas vezes aparece a letra ‘A’ e sim se existe a letra ‘A’.

alfabeto = ABC;

texto = Um texto Qualquer;

array = [‘A’, B’, C’]; os caracteres do alfabeto

ai vem o loop

o texto possui a letra ‘A’
o texto possui a letra ‘B’
o texto pessui a letra ‘C’

termina o loop

pronto, acabou, nesse caso seriam feitas 3 iteracoes no array e nao o tamanho do array x o tamanho do texto.

ViniGodoy

lsjunior, o método contains tem dentro dele um for. Se a letra não existir no alfabeto, o texto inteiro será percorrido.

Portanto, seu método irá percorrer o texto inteiro para cada letra que não existir, e irá percorrer o texto do início até a primeira ocorrência das letras que existirem.
Isso repetido para cada letra do alfabeto.

O método que postei percorre o texto uma única vez, e testa simultaneamente todas as letras do alfabeto. Ele também não perde tempo verificando se a letra já existe ou não no array. E a contagem é apenas um benefício adicional. No primeiro exemplo, usei um boolean.

A única coisa que poderia ser usada para tornar o meu algoritmo mais eficiente, no caso de eliminar a contagem, é ter um contador para saber quantas letras já foram encontradas dentro do alfabeto. Quando esse contador chegar a 0, poderia interromper o algoritmo. Isso evitaria que o algoritmo perdesse tempo percorrendo um texto muito longo, caso todas as letras já tivessem sido encontradas.

AlexandreGama

Então lsjunior, aí é que está o problema!
Não temos 3 iterações pois pra você identificar os caracteres, você percorrerá a String inteira de qualquer forma.
Seja com for ou com contains.

Para cada letra que você for verificar será um novo for, ou seja, você percorrerá até que a letra seja encontrada.
Logo, você não teria somente 3 iterações.

Abraços!

L

Eu sei como funciona o indexOf. Fiz o exemplo para fins didaticos, por que enviar muito código um pouco complexo para um professor é o mesmo que dizer que não foi ele que fez. Existem ínumeras formas de resolver aquele problema e eu fiz da forma que achei o mais simples possível.

ViniGodoy

Pra quem duvida, eis a implementação dos métodos contains e indexOf:

/**
     * Returns true if and only if this string contains the specified
     * sequence of char values.
     *
     * @param s the sequence to search for
     * @return true if this string contains <code>s</code>, false otherwise
     * @throws NullPointerException if <code>s</code> is <code>null</code>
     * @since 1.5
     */
    public boolean contains(CharSequence s) {
        return indexOf(s.toString()) > -1;
    }
/**
     * Returns the index within this string of the first occurrence of the
     * specified substring. The integer returned is the smallest value
     * <i>k</i> such that:
     * <blockquote><pre>
     * this.startsWith(str, <i>k</i>)
     * </pre></blockquote>
     * is <code>true</code>.
     *
     * @param   str   any string.
     * @return  if the string argument occurs as a substring within this
     *          object, then the index of the first character of the first
     *          such substring is returned; if it does not occur as a
     *          substring, <code>-1</code> is returned.
     */
    public int indexOf(String str) {
    return indexOf(str, 0);
    }
/**
     * Returns the index within this string of the first occurrence of the
     * specified substring, starting at the specified index.  The integer
     * returned is the smallest value <tt>k</tt> for which:
     * <blockquote><pre>
     *     k >= Math.min(fromIndex, this.length()) && this.startsWith(str, k)
     * </pre></blockquote>
     * If no such value of <i>k</i> exists, then -1 is returned.
     *
     * @param   str         the substring for which to search.
     * @param   fromIndex   the index from which to start the search.
     * @return  the index within this string of the first occurrence of the
     *          specified substring, starting at the specified index.
     */
    public int indexOf(String str, int fromIndex) {
        return indexOf(value, offset, count,
                       str.value, str.offset, str.count, fromIndex);
    }

Finalmente, aqui está o for escondido:

/**
     * Code shared by String and StringBuffer to do searches. The
     * source is the character array being searched, and the target
     * is the string being searched for.
     *
     * @param   source       the characters being searched.
     * @param   sourceOffset offset of the source string.
     * @param   sourceCount  count of the source string.
     * @param   target       the characters being searched for.
     * @param   targetOffset offset of the target string.
     * @param   targetCount  count of the target string.
     * @param   fromIndex    the index to begin searching from.
     */
    static int indexOf(char[] source, int sourceOffset, int sourceCount,
                       char[] target, int targetOffset, int targetCount,
                       int fromIndex) {
	if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
	}
    	if (fromIndex < 0) {
    	    fromIndex = 0;
    	}
	if (targetCount == 0) {
	    return fromIndex;
	}

        char first  = target[targetOffset];
        int max = sourceOffset + (sourceCount - targetCount);

        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            /* Look for first character. */
            if (source[i] != first) {
                while (++i <= max && source[i] != first);
            }

            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                int j = i + 1;
                int end = j + targetCount - 1;
                for (int k = targetOffset + 1; j < end && source[j] ==
                         target[k]; j++, k++);

                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        return -1;
    }
AlexandreGama

Poxa, de vez em quando escrevo enquanto o Vinny também está postando e acabo
falando algo parecido. Sorry!

Mas o Vinny explicou também a idéia do algoritmo dele! :slight_smile:

Até mais!

ViniGodoy

E o que tem de complexo no meu código? É um uso básico da tabela ascii.
Não envolve concatenação de Strings, ou uso de métodos específicos da API.

Provavelmente é o que o professor está esperando, se ele ensinou como o char funciona.

AlexandreGama

E o que tem de complexo no meu código? É um uso básico da tabela ascii.
Provavelmente é o que o professor está esperando, se ele ensinou como o char funciona.

Isso que quis dizer lsjunior sobre as minhas antigas aulas de C na faculdade.
Normalmente o professor ensina o conceito de char e pede exatamente como o Vinny falou.

Na verdade nem estava discutindo o seu código em si, somente dei um toque sobre as iterações
que você mencionou.

Abraços!

L

Certo.

ViniGodoy

Pra quem não entendeu como meu código funciona.

Toda caracter, em java, é também um número. Esse número é baseado numa tabela, chamada Unicode (antigamente Ascii). Se você consultar essa tabela, verá que lá o valor da letra A é 65, B é 66, C é 67 e assim por diante.

O código então cria um array de letras encontradas, com posição de 0 até 25 (uma para cada letra do alfabeto).

Quando encontro uma letra, subtraio 65 do seu valor. Isso fará com que eu obtenha 0 para a letra A (que vale 65), 1 para letra B, 2 para a letra C, e assim por diante. Esse é o índice da letra no array das letras encontradas. Com esse índice na mão, basta marcar ali “true” indicando que achei a letra.

O mesmo processo funciona para contagem. A única diferença é que ao invés do array representar letras encontradas, eu apenas somo 1 à contagem.

O código aparentemente é mais complexo por ter uma continha de menos simples, e por considerar o valor ascii da letra. Claro, se você não conhece essa tabela, ele será ininteligível. Vai da matéria que seu professor está passando.

Criado 20 de outubro de 2010
Ultima resposta 20 de out. de 2010
Respostas 28
Participantes 7