Contar caracteres distintos em uma string no Object pascal
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
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;
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).
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;