1
votes

Good day, I have made this code that is supposed to count distinct characters in a string, my code was tested by several inputs but it failed to count characters in this input :

zcinitufxoldnokacdvtmdohsfdjepyfioyvclhmujiqwvmudbfjzxjfqqxjmoiyxrfsbvseawwoyynn 

it has almost 80 characters and the maximum string that Pascal can read has the length of 256 character. I couldn't find a better algorithm to solve this, so I'm looking for some help from the experts in this field, or anyone that is open to share knowledge.

I guess that my code is jumping a character in each loop.

Here is my code :

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; 

I sincerely appreciate your help.

3
Since you always increment the loop index, you will have to decrement it when a dupe is found. - LU RD
"the maximum string that Pascal can read has the length of 256 character" - that is true only for ShortString, not for string. - Remy Lebeau
I told you in your earlier question that delete()'ing characters from a string would affect your loops, skipping characters. You haven't fixed that properly yet. - Remy Lebeau
@CouldnoTB-Zone don't edit your question to post the solution. If you want to show what works for you, post it as an answer instead. You can answer your own questions. - Remy Lebeau
I would simply create a sorted string list that does not allow duplicates, then add each character to a different entry in the list. At the end you can get the count of list entries. - Ron Maupin

3 Answers

2
votes

If you only need to get the number of distinct character in some string you can use something simple as this:

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;

If you also need a information of which characters are present in your input string you could instead of using local DistinctChars string variable pass a string as var pamaeter to your function like so:

//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;

But if you also want information about how many of each characters there are in your input string then you will have to use some data structure for your result that allows storing pairs of data like TDictionary or perhaps array of records where each record stores pair of information (character and number of occurrences).

1
votes

You are not properly accounting for a string's length changing when you delete() characters from it.

If username is empty, you end up accessing invalid characters since your repeat loop tries to access a character at index 1, which doesn't exist. Actually, you end up in an endless loop since i = length_usernameaux will always be False since i starts at 1 and increments upwards but length_usernameaux is always 0 (well, at least, the loop runs until i overflows to a negative value and eventually increments back up to 0, but by that time you have likely crashed your code way before that happens).

If username is not empty, you increment i on every loop iteration, which will skip the next character when delete()'ing a character at i. i needs to stay at the same index whenever a character is delete()'d, since the next character will slide down to occupy the index of the character that was just delete'd. Increment i only when NOT delete()'ing a character.

Try this instead:

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;
1
votes

I have managed to fix it my way, in this way which is thanks to the help of you. All i did is to decreased the index by one after deleting the duplicated character. Like this :

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;