Contar caracteres distintos em uma string no Object pascal

Sep 11 2020

Bom dia, eu fiz este código que deveria contar caracteres distintos em uma string, meu código foi testado por várias entradas, mas falhou ao contar caracteres nesta entrada:

zcinitufxoldnokacdvtmdohsfdjepyfioyvclhmujiqwvmudbfjzxjfqqxjmoiyxrfsbvseawwoyynn 

tem quase 80 caracteres e a string máxima que Pascal pode ler tem o comprimento de 256 caracteres. Não consegui encontrar um algoritmo melhor para resolver isso, então estou procurando ajuda de especialistas na área, ou de alguém que esteja aberto para compartilhar conhecimento.

Eu acho que meu código está pulando um personagem em cada loop.

Aqui está o meu código:

function freq(char: char; username : String): Integer;
var 
   i, auxfreq: Integer;
begin
    auxfreq:= 0;
    for i:= 1 to length(username) do
        if char = username[i] then
            auxfreq:= auxfreq + 1;
            //writeln(freq);    
    freq:= auxfreq;         
end; 

function OddUserName(username : String): Boolean;
var
    usernameaux : String;
    length_usernameaux, i : Integer;
    Result : Boolean;

begin
    Result:= false;
    usernameaux:= username;
    i:= 0;
    repeat
        i +=1; 
        length_usernameaux:= length(usernameaux);
        if freq(usernameaux[i], usernameaux) <> 1 then 
            delete(usernameaux, i, 1);
    until i = length_usernameaux;
    // length(usernameaux) is supposed to be the number of the distinct characters.
    
    {if length(usernameaux) mod 2 <> 0 then // you will have to ignore this.
        Result:= true; // odd}

    //writeln(usernameaux); 
    //writeln(length(usernameaux));
    OddUserName:= Result; // ignore this too    
end; 

Agradeço sinceramente sua ajuda.

Respostas

1 RemyLebeau Sep 11 2020 at 02:56

Você não está contabilizando adequadamente a alteração do comprimento de uma string quando você os delete()caracteres dela.

Se usernameestiver vazio, você acaba acessando caracteres inválidos, pois seu repeatloop tenta acessar um caractere no índice 1, que não existe. Na verdade, você termina em um loop infinito, pois i = length_usernameauxsempre será False, pois icomeça em 1 e aumenta para cima, mas length_usernameauxé sempre 0 (bem, pelo menos, o loop é executado até iestourar para um valor negativo e, eventualmente, incrementar de volta até 0, mas em dessa vez, você provavelmente travou seu código antes disso acontecer).

Se usernamenão estiver vazio, você incrementa ia cada iteração do loop, o que pulará o próximo caractere quando estiver usando delete()um caractere em i. iprecisa ficar no mesmo índice sempre que um caractere for delete()'d, pois o próximo caractere deslizará para baixo para ocupar o índice do caractere que acabou de ser delete' d. Aumentar iapenas quando NÃO estiver em delete()um personagem.

Em vez disso, tente isto:

function freq(charToFind: char; username : String): Integer;
var 
  i, auxfreq: Integer;
begin
  auxfreq := 0;
  for i := 1 to Length(username) do
  begin
    if charToFind = username[i] then
      auxfreq := auxfreq + 1;
  end;
  //writeln(freq);    
  freq := auxfreq;         
end; 

function OddUserName(username : String): Boolean;
var
  usernameaux : String;
  length_usernameaux, i : Integer;
  Result : Boolean;
begin
  Result := false;
  usernameaux := username;
  length_usernameaux := Length(usernameaux);
  i := 1;
  while i <= length_usernameaux do
  begin
    if freq(usernameaux[i], usernameaux) > 1 then
    begin
      Delete(usernameaux, i, 1);
      length_usernameaux := length_usernameaux - 1;
    end else
    begin
      i = i + 1;
    end;
  end;
  // length_usernameaux is supposed to be the number of the distinct characters.
    
  {if length_usernameaux mod 2 <> 0 then // you will have to ignore this.
    Result := true; // odd}

  //writeln(usernameaux); 
  //writeln(length_usernameaux);
  OddUserName := Result; // ignore this too    
end;
2 SilverWarior Sep 11 2020 at 04:03

Se você só precisa obter o número de caracteres distintos em alguma string, pode usar algo simples como isto:

function CountDistinctCharacters(InputString: string): Integer;
var I: Integer;
    //String for storing all distinct characters
    DistinctChars: string;
begin
  //Loop trough every character in input string
  for I := 1 to Length(InputString) do
  begin
    //Use Pos function to find position of specific character in DistinctChars string
    //Function returns 0 if character is not found
    if Pos(InputString[I], DistinctChars) = 0 then
    begin
      //If character isn't found in DistinctChars string add it to it
      DistinctChars := DistinctChars+InputString[I];
    end;
  end;
  //Finaly check the lenght of DistinctChars string to get the number of distinct character
  //found and return it as function result
  Result := Length(DistinctChars);
end;

Se você também precisar de uma informação de quais caracteres estão presentes em sua string de entrada, você poderia, em vez de usar uma DistinctCharsvariável de string local , passar uma string como parâmetro var para sua função, desta forma:

//Pass external string as var parameter to your function in order to allow function to
//fill it with all distinct characters
function CountDistinctCharacters(InputString: string; var DistinctChars: string): Integer;
var I: Integer;
begin
  //Loop trough every character in input string
  for I := 1 to Length(InputString) do
  begin
    //Use Pos function to find position of specific character in DistinctChars string
    //Function returns 0 if character is not found
    if Pos(InputString[I], DistinctChars) = 0 then
    begin
      //If character isn't found in DistinctChars string add it to it
      DistinctChars := DistinctChars+InputString[I];
    end;
  end;
  //Finaly check the lenght of DistinctChars string to get the number of distinct character
  //found and return it as function result
  Result := Length(DistinctChars);
end;

Mas se você também quiser informações sobre quantos de cada caractere existem em sua string de entrada, você terá que usar alguma estrutura de dados para o seu resultado que permite armazenar pares de dados como TDictionary ou talvez um array de registros onde cada registro armazena par de informações (personagem e número de ocorrências).

1 CouldnoTB-Zone Sep 11 2020 at 04:59

Consegui consertar do meu jeito, desta forma graças à ajuda de vocês. Tudo o que fiz foi diminuir o índice em um depois de excluir o caractere duplicado. Como isso :

function OddUserName(username : String): Boolean;
var
    usernameaux : String;
    length_usernameaux, i : Integer;
    //Result : Boolean;

begin
    Result:= false;
    usernameaux:= username;
    i:= 0;
    repeat
        i +=1; 
        length_usernameaux:= length(usernameaux);
        if freq(usernameaux[i], usernameaux) <> 1 then 
        begin   
            delete(usernameaux, i, 1);
            i-=1; // <----- added 
        end;    
    until i = length_usernameaux;
    
    if length(usernameaux) mod 2 <> 0 then
        Result:= true; // odd

    //writeln(usernameaux); 
    //writeln(length(usernameaux));
    OddUserName:= Result;   
end;