Учитывая строку, найдите ее первый неповторяющийся символ только в одном сканировании

учитывая строку, найдите в ней первый неповторяющийся символ. Например, если входной строкой является "GeeksforGeeks", то вывод должен быть "f".

мы можем использовать строковые символы в качестве индекса и построить массив count. Ниже приведен алгоритм.

1) сканируйте строку слева направо и постройте массив count или HashMap.
2) снова сканируйте строку слева направо и проверьте количество каждого характер, если вы находите элемент, то который отсчет 1, вернуть его.

выше алгоритм с GeeksForGeeks

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

import java.util.HashMap;
import java.util.Scanner;

/**
 *
 * @author Neelabh
 */
public class FirstNonRepeatedCharacter {
    public static void main(String [] args){
        Scanner scan=new Scanner(System.in);
        String string=scan.next();
        int len=string.length();
        HashMap<Character, Integer> hashMap=new HashMap<Character, Integer>();
        //First Scan
        for(int i = 0; i <len;i++){
            char currentCharacter=string.charAt(i);
            if(!hashMap.containsKey(currentCharacter)){
                hashMap.put(currentCharacter, 1);
            }
            else{
                hashMap.put(currentCharacter, hashMap.get(currentCharacter)+1);
            }
        }
        // Second Scan
        boolean flag=false;
        char firstNonRepeatingChar = 0;
        for(int i=0;i<len;i++){
                char c=string.charAt(i);
                if(hashMap.get(c)==1){
                    flag=true;
                    firstNonRepeatingChar=c;
                    break;
                }
        }
        if(flag==true)
            System.out.println("firstNonRepeatingChar is "+firstNonRepeatingChar);
        else
            System.out.println("There is no such type of character");
    }    
}

GeeksforGeeks также предлагают эффективный метод, но я думаю, что это также два сканирования. Следующее решение от GeeksForGeeks

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#define NO_OF_CHARS 256

// Structure to store count of a character and index of the first
// occurrence in the input string
struct countIndex {
   int count;
   int index;
};

/* Returns an array of above structure type. The size of
   array is NO_OF_CHARS */
struct countIndex *getCharCountArray(char *str)
{
   struct countIndex *count =
        (struct countIndex *)calloc(sizeof(countIndex), NO_OF_CHARS);
   int i;

   // This is First Scan

   for (i = 0; *(str+i);  i++)
   {
      (count[*(str+i)].count)++;

      // If it's first occurrence, then store the index
      if (count[*(str+i)].count == 1)
         count[*(str+i)].index = i;
   }
   return count;
}

/* The function returns index of the first non-repeating
    character in a string. If all characters are repeating
    then reurns INT_MAX */
int firstNonRepeating(char *str)
{
  struct countIndex *count = getCharCountArray(str);
  int result = INT_MAX, i;

  //Second Scan
  for (i = 0; i < NO_OF_CHARS;  i++)
  {
    // If this character occurs only once and appears
    // before the current result, then update the result
    if (count[i].count == 1 && result > count[i].index)
       result = count[i].index;
  }

  free(count); // To avoid memory leak
  return result;
}


/* Driver program to test above function */
int main()
{
  char str[] = "geeksforgeeks";
  int index =  firstNonRepeating(str);
  if (index == INT_MAX)
    printf("Either all characters are repeating or string is empty");
  else
   printf("First non-repeating character is %c", str[index]);
  getchar();
  return 0;
}

27 ответов


вы можете хранить 2 массива: количество каждого символа и первого вхождения(и заполнить оба из них во время первого сканирования). Тогда второе сканирование будет ненужным.


используйте строковые функции java, тогда вы найдете решение только в одном цикле for Пример приведен ниже

import java.util.Scanner;
public class firstoccurance {
public static void main(String args[]){
char [] a ={'h','h','l','l','o'};
//Scanner sc=new Scanner(System.in);
String s=new String(a);//sc.next();
char c;
int i;
int length=s.length();
for(i=0;i<length;i++)
{
    c=s.charAt(i);
    if(s.indexOf(c)==s.lastIndexOf(c))
    {
        System.out.println("first non repeating char in a string   "+c);
        break;
    }
    else if(i==length-1)
    {
        System.out.println("no single char");
    }
}
}
}

в следующем решении я объявляю один класс CharCountAndPosition который хранит firstIndex и frequencyOfchar. Во время чтения строки символьно firstIndex сохраняет первую встречу символов, а frequencyOfchar сохраняет общее вхождение символов.

мы сделаем массив CharCountAndPosition Шаг 1 и инициализировать его Шаг 2.
Во время сканирования строки инициализируйте firstIndex и frequencyOfchar для каждого персонажа Шаг 3.
Теперь в Шаг 4 проверьте массив CharCountAndPosition, найдите символ с частотой==1 и минимальным firstIndex
за все время сложность равна O (n+256), где n-размер строки. O(n+256) эквивалентно O (n) потому что 256-это константа. пожалуйста, найдите решение этого на ideone

public class FirstNonRepeatedCharacterEfficient {
        public static void main(String [] args){
            // step1: make array of CharCountAndPosition.
            CharCountAndPosition [] array=new CharCountAndPosition[256];

            // step2: Initialize array with object of CharCountAndPosition. 
            for(int i=0;i<256;i++)
            {
                array[i]=new CharCountAndPosition();
            }

            Scanner scan=new Scanner(System.in);
            String str=scan.next();
            int len=str.length();
            // step 3
            for(int i=0;i<len;i++){
                char c=str.charAt(i);
                int index=c-'a';            
                int frequency=array[index].frequencyOfchar;
                if(frequency==0)
                    array[index].firstIndex=i;
                array[index].frequencyOfchar=frequency+1;    
                //System.out.println(c+" "+array[index].frequencyOfchar);
            }
            boolean flag=false;
            int firstPosition=Integer.MAX_VALUE;
            for(int i=0;i<256;i++){   

                // Step4         
                if(array[i].frequencyOfchar==1){
                    //System.out.println("character="+(char)(i+(int)'a'));
                    if(firstPosition> array[i].firstIndex){                    
                        firstPosition=array[i].firstIndex;
                        flag=true;
                    }
                }            
            }
            if(flag==true)
                System.out.println(str.charAt(firstPosition));
            else
                System.out.println("There is no such type of character");
        } 
    }
    class CharCountAndPosition{
        int firstIndex;
        int frequencyOfchar;
    }

решение в javascript с таблицей поиска:

var sample="It requires two scan of an array I want to find first non repeating character in only one scan";
var sampleArray=sample.split("");
var table=Object.create(null);
sampleArray.forEach(function(char,idx){
  char=char.toLowerCase();
  var pos=table[char];
  if(typeof(pos)=="number"){
    table[char]=sampleArray.length;  //a duplicate found; we'll assign some invalid index value to this entry and discard these characters later
    return;
  }
  table[char]=idx;  //index of first occurance of this character
});
var uniques=Object.keys(table).filter(function(k){
  return table[k]<sampleArray.length; 
}).map(function(k){
  return {key:k,pos:table[k]};
});
uniques.sort(function(a,b){
  return a.pos-b.pos;
});
uniques.toSource();         //[{key:"q", pos:5}, {key:"u", pos:6}, {key:"d", pos:46}, {key:"p", pos:60}, {key:"g", pos:66}, {key:"h", pos:69}, {key:"l", pos:83}]
(uniques.shift()||{}).key;  //q

после c prog добавьте значение char в "count", если char не произошел раньше, удаляет значение char из "count", если char произошел раньше. В конце я получаю "счет", который имеет определенное значение char, которое указывает, что это был char!

//TO DO:
//If multiple unique char occurs, which one is occurred before? 
//Is is possible to get required values (1,2,4,8,..) till _Z_ and _z_?

#include <stdio.h>

#define _A_ 1
#define _B_ 2
#define _C_ 4 
#define _D_ 8
//And so on till _Z

//Same for '_a' to '_z'

#define ADDIFNONREP(C) if(count & C) count = count & ~C; else count = count | C; break;

char getNonRepChar(char *str)
{
        int i = 0, count = 0;
        for(i = 0; str[i] != ''; i++)
        {
                switch(str[i])
                {
                        case 'A':
                                ADDIFNONREP(_A_);
                        case 'B':
                                ADDIFNONREP(_B_);
                        case 'C':
                                ADDIFNONREP(_C_);
                        case 'D':
                                ADDIFNONREP(_D_);
                        //And so on
                        //Same for 'a' to 'z'
                }
        }
        switch(count)
        {
                case _A_:
                        return 'A';
                case _B_:
                        return 'B';
                case _C_:
                        return 'C';
                case _D_:
                        return 'D';
                //And so on
                //Same for 'a' to 'z'
        }
}

int main()
{
        char str[] = "ABCDABC";
        char c = getNonRepChar(str);
        printf("%c\n", c); //Prints D
        return 0;
} 

вы можете поддерживать очередь ключей по мере их добавления на хэш-карту (вы добавляете свой ключ в очередь, если вы добавляете новый ключ на хэш-карту). После сканирования строк используйте очередь для получения порядка ключей по мере их добавления на карту. Эта функциональность именно то, что Java standard library class OrderedHashMap делает.


вот мой взгляд на проблему.

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

NSMutableSet *repeated = [[NSMutableSet alloc] init]; //Hashset

NSMutableArray *nonRepeated = [[NSMutableArray alloc] init]; //Array

for (int i=0; i<[test length]; i++) {

    NSString *currentObj = [NSString stringWithFormat:@"%c", [test characterAtIndex:i]]; //No support for primitive data types.

    if ([repeated containsObject:currentObj]) {
        [nonRepeated removeObject:currentObj];// in obj-c nothing happens even if nonrepeted in nil
        continue;
    }

    [repeated addObject:currentObj];

    [nonRepeated addObject:currentObj];

}

NSLog(@"This is the character %@", [nonRepeated objectAtIndex:0]);

если вы можете ограничить себя строками символов ASCII, я бы рекомендовал таблица вместо хэш-таблицы. Эта таблица поиска будет иметь только 128 записей.

возможный подход будет следующим.

начнем с пустого очереди Q (может быть реализован с использованием связанных списков) и таблица T. Для персонажа ch, T[ch] сохраняет указатель на узел очереди, содержащий символ ch и индекс первого вхождения ch в строке. Первоначально все записи T are NULL.

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

читать строку посимвольно. Если ith символ ch, проверьте, если T[ch] = NULL. Если это так, то это первое появление ch в строке. Затем добавьте узел для ch содержащий индекс i в очередь.

если T[ch] не NULL, это повторяющийся характер. Если узел указывает на T[ch] уже был удален (т. е. удалены флаг узла установлен), тогда ничего не нужно делать. В противном случае удалите узел из очереди, манипулируя указателями предыдущего и следующего узлов. Также установите удалены флаг узла, чтобы указать, что узел теперь удален. Примечание что мы не освобождаем/удаляем узел на этом этапе, и мы не устанавливаем T[ch] на NULL.

если мы продолжим таким образом, узлы для все повторяющиеся символы будут удалены из очереди. The удалены флаг используется для обеспечения того, чтобы ни один узел не удалялся дважды из очереди, если символ встречается более двух раз.

после полной обработки строки первый узел связанного списка будет содержать код символа, а также индекс первого неповторяющегося символа. Затем память может быть освобождена путем итерации по записям таблицы подстановки T и освобождая неNULL записи.

вот это Си. Здесь вместо удалены флаг, я поставил prev и next указатели текущего узла NULL когда он будет удален, и проверьте это, чтобы увидеть, был ли узел уже удален.

#include <stdio.h>
#include <stdlib.h>

struct queue_node {
    int ch;
    int index;
    struct queue_node *prev;
    struct queue_node *next;
};

void print_queue (struct queue_node *head);

int main (void)
{
    int i;
    struct queue_node *lookup_entry[128];
    struct queue_node *head;
    struct queue_node *last;
    struct queue_node *cur_node, *prev_node, *next_node;

    char str [] = "GeeksforGeeks";

    head = malloc (sizeof (struct queue_node));
    last = head;
    last->prev = last->next = NULL;

    for (i = 0; i < 128; i++) {
        lookup_entry[i] = NULL;
    }

    for (i = 0; str[i] != ''; i++) {
        cur_node = lookup_entry[str[i]];

        if (cur_node != NULL) {
            /* it is a repeating character */
            if (cur_node->prev != NULL) {
                /* Entry has not been removed. Remove it from the queue. */
                prev_node = cur_node->prev;
                next_node = cur_node->next;

                prev_node->next = next_node;
                if (next_node != NULL) {
                    next_node->prev = prev_node;
                } else {
                    /* Last node was removed */
                    last = prev_node;
                }

                cur_node->prev = NULL;
                cur_node->next = NULL;
                /* We will not free the node now. Instead, free
                 * all nodes in a single pass afterwards.
                 */ 
            }
        } else {
            /* This is the first occurence - add an entry to the queue */
            struct queue_node *newnode = malloc (sizeof(struct queue_node));

            newnode->ch = str[i];
            newnode->index = i;
            newnode->prev = last;
            newnode->next = NULL;
            last->next = newnode;
            last = newnode;

            lookup_entry[str[i]] = newnode;
        }
        print_queue (head);
    }

    last = head->next;
    while (last != NULL) {
        printf ("Non-repeating char: %c at index %d.\n", last->ch, last->index);
        last = last->next;
    }

    /* Free the queue memory */
    for (i = 0; i < 128; i++) {
        if (lookup_entry[i] != NULL) {
            free (lookup_entry[i]);
            lookup_entry[i] = NULL;
        }
    }
    free (head);

    return (0);
}

void print_queue (struct queue_node *head) {
    struct queue_node *tmp = head->next;

    printf ("Queue: ");
    while (tmp != NULL) {
        printf ("%c:%d ", tmp->ch, tmp->index);
        tmp = tmp->next;
    }
    printf ("\n");
}

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

class test{
    public static void main(String args[]){
        String s="STRESST";//Your input can be given here.
        char a[]=new char[s.length()];
    for(int i=0;i<s.length();i++){
        a[i]=s.charAt(i);
    }

    for(int i=0;i<s.length();i++){
        int flag=0;
       for(int j=0;j<s.length();j++){
            if(a[i]==a[j]){
             flag++;
            }
       }

        if(flag==1){
         System.out.println(a[i]+" is not repeated");
         break;
        }
    }
    }
}

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


Это можно сделать за одно сканирование с помощью метода substring. Сделайте это так:

String str="your String";<br>
String s[]= str.split("");<br>
int n=str.length();<br>
int i=0;<br><br>

for(String ss:s){
  if(!str.substring(i+1,n).contains(ss)){
    System.out.println(ss);
  }
}

Это будет иметь самую низкую сложность и будет искать его даже без завершения одного полного сканирования.


добавить каждому символу поиска HashSet и проверить поиска HashSet.add () возвращает true, если возвращает false ,а затем удаляет символ из hashset. Затем получение первого значения хэш-набора даст вам первый не повторяющийся символ. :

 for(i=0;i<str.length;i++)
{
 HashSet hashSet=new HashSet<>()
 if(!hashSet.add(str[i))
   hashSet.remove(str[i])
 }
 hashset.get(0) will give the non repeated character.

У меня есть эта программа, которая является более простой, это не использование каких-либо структур данных

public static char findFirstNonRepChar(String input){
    char currentChar = '';
    int len = input.length();
    for(int i=0;i<len;i++){
        currentChar = input.charAt(i);
        if((i!=0) && (currentChar!=input.charAt(i-1)) && (i==input.lastIndexOf(currentChar))){
            return currentChar;
        }
    }
    return currentChar;
}

простая (не хэшированная) версия...

public static String firstNRC(String s) {
    String c = "";
    while(s.length() > 0) {
        c = "" + s.charAt(0);
        if(! s.substring(1).contains(c)) return c;
        s = s.replace(c, "");
    }
    return "";
}

или

public static char firstNRC(String s) {
    s += " ";
    for(int i = 0; i < s.length() - 1; i++) 
        if( s.split("" + s.charAt(i)).length == 2 ) return s.charAt(i);
    return ' ';
}

/ / это простая логика поиска первого неповторяющегося символа....

public static void main(String[] args) {

        String s = "GeeksforGeeks";

        for (int i = 0; i < s.length(); i++) {

            char begin = s.charAt(i);
            String begin1 = String.valueOf(begin);
            String end = s.substring(0, i) + s.substring(i + 1);

            if (end.contains(begin1));
            else {
                i = s.length() + 1;
                System.out.println(begin1);
            }

        }

    }

@Test
public void testNonRepeadLetter() {
    assertEquals('f', firstNonRepeatLetter("GeeksforGeeks"));
    assertEquals('I', firstNonRepeatLetter("teststestsI"));
    assertEquals('1', firstNonRepeatLetter("123aloalo"));
    assertEquals('o', firstNonRepeatLetter("o"));

}

private char firstNonRepeatLetter(String s) {
    if (s == null || s.isEmpty()) {
        throw new IllegalArgumentException(s);
    }
    Set<Character> set = new LinkedHashSet<>();

    for (int i = 0; i < s.length(); i++) {
        char charAt = s.charAt(i);
        if (set.contains(charAt)) {
            set.remove(charAt);
        } else {
            set.add(charAt);
        }
    }
    return set.iterator().next();
}

вот проверенный код на java. обратите внимание, что возможно, что не повторяющийся символ не найден, и для этого мы возвращаем "0"

// find first non repeated character in a string
static char firstNR( String str){
    int i, j, l;
    char letter;
    int[] k = new int[100];

    j = str.length();

    if ( j > 100) return '0';

    for (i=0; i< j; i++){
        k[i] = 0;
    }

    for (i=0; i<j; i++){
        for (l=0; l<j; l++){
            if (str.charAt(i) == str.charAt(l))
                k[i]++;
        }
    }

    for (i=0; i<j; i++){
        if (k[i] == 1)
            return str.charAt(i);
    }

    return '0';

вот логика, чтобы найти первую неповторяемую букву в строке.

String name = "TestRepeat";
        Set <Character> set = new LinkedHashSet<Character>();
        List<Character> list = new ArrayList<Character>();
        char[] ch = name.toCharArray();
        for (char c :ch) {
            set.add(c);
            list.add(c);
        }
        Iterator<Character> itr1 = set.iterator();
        Iterator<Character> itr2= list.iterator();

        while(itr1.hasNext()){
            int flag =0;
            Character  setNext= itr1.next();
            for(int i=0; i<list.size(); i++){
                Character listNext= list.get(i);
                if(listNext.compareTo(setNext)== 0){
                    flag ++;
                }
            }

            if(flag==1){
                System.out.println("Character: "+setNext);
                break;
            }
        }

это очень легко....вы можете сделать это без коллекции на java..

public class FirstNonRepeatedString{

    public static void main(String args[]) {
        String input ="GeeksforGeeks";
        char process[] = input.toCharArray();
        boolean status = false;
        int index = 0;
        for (int i = 0; i < process.length; i++) {
            for (int j = 0; j < process.length; j++) {

                if (i == j) {
                    continue;
                } else {
                    if (process[i] == process[j]) {
                        status = false;
                        break;
                    } else {
                        status = true;
                        index = i;
                    }
                }

            }
             if (status) {
            System.out.println("First non-repeated string is : " + process[index]);
            break;
        } 
        }
    }
}

мы можем создать LinkedHashMap, имеющий каждый символ из строки, и это соответствующий счетчик. А затем пройдите по карте, когда вы столкнетесь с char с count as 1 верните этот символ. Ниже приведена функция для того же.

private static char findFirstNonRepeatedChar(String string) {
    LinkedHashMap<Character, Integer> map = new LinkedHashMap<>();
    for(int i=0;i< string.length();i++){
        if(map.containsKey(string.charAt(i)))
            map.put(string.charAt(i),map.get(string.charAt(i))+1);
        else
            map.put(string.charAt(i),1);
    }

    for(Entry<Character,Integer> entry : map.entrySet()){
        if(entry.getValue() == 1){
            return entry.getKey();
        }
    }
    return ' ';
}

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

    import java.util.*;
    class demo
    {
    public static void main(String args[])
     {
          String str="GeekGsQuizk";
          HashMap  <Character,Integer>hm=new LinkedHashMap<Character,Integer>();

                    for(int i=0;i<str.length();i++)
                      {
                       if(!hm.containsKey(str.charAt(i)))
                       hm.put(str.charAt(i),1);
                       else
                       hm.put(str.charAt(i),hm.get(str.charAt(i))+1);
                      }

           for (Character key : hm.keySet())
               {
                      if(hm.get(key)==1)
                   { 
                   System.out.println(key);
                   System.exit(0) ; 
                   }         

               }

         }
       }

Я знаю, что это происходит на год позже, но я думаю, что если вы используете LinkedHashMap в своем решении вместо использования HashMap, у вас будет гарантированный порядок на результирующей карте, и вы можете напрямую вернуть ключ с соответствующим значением как 1.

Не уверен, что это то, что вы хотели, хотя, как вам придется перебирать карту (не строку) после того, как вы закончите заполнять ее, но только мои 2 цента.

с уважением,

-Вини


поиск первого неповторяющегося символа за один проход O ( n) без использования методов indexOf и lastIndexOf

package nee.com;
public class FirstNonRepeatedCharacterinOnePass {

    public static void printFirstNonRepeatedCharacter(String str){
        String strToCaps=str.toUpperCase();
        char ch[]=strToCaps.toCharArray();
        StringBuilder sb=new StringBuilder();
        // ASCII range for A-Z ( 91-65 =26)
        boolean b[]=new boolean[26];

        for(int i=0;i<ch.length;i++){
            if(b[ch[i]-65]==false){
                b[ch[i]-65]=true;               
            }
            else{
                //add repeated char to StringBuilder
                sb.append(ch[i]+"");
            }
        }
        for(int i=0;i<ch.length;i++){
            // if char is not there in StringBuilder means it is non repeated
            if(sb.indexOf(ch[i]+"")==-1){
                System.out.println(" first non repeated  in lower case ...."+Character.toLowerCase((ch[i])));
            break;
            }               
        }

    }
    public static void main(String g[]){
        String str="abczdabddcn";
        printFirstNonRepeatedCharacter(str);
    }

}

Я сделал то же самое используя LinkedHashSet. Ниже приведен фрагмент кода:

System.out.print("Please enter the string :");
str=sc.nextLine();
if(null==str || str.equals("")) {
   break;
}else {
   chArr=str.toLowerCase().toCharArray();
   set=new LinkedHashSet<Character>();
   dupSet=new LinkedHashSet<Character>();
   for(char chVal:chArr) {
    if(set.contains(chVal)) {
        dupSet.add(chVal);
    }else {
        set.add(chVal);
    }
   }
   set.removeAll(dupSet);
   System.out.println("First unique :"+set.toArray()[0]);
}

Вы можете найти этот вопрос здесь

для кода приведенного ниже алгоритма обратитесь этой link (моя реализация с тестовыми случаями)

С помощью linkedlist в сочетании с хранилище HashMap

У меня есть решение, которое решает его в O (n) время один проход массива и O (1) пробел Inreality - > O (1) пространство O (26) пространство

алгоритм

1) каждый раз, когда вы посещаете персонажа в первый раз

создайте узел для linkedList (сохранение этого символа).Добавьте его в конце списка lnkedList.Добавьте запись в hashMap, хранящую для недавно добавленного charater адрес узла в связанном списке, который был до этого символа.Если символ добавляется к пустому связанному списку, храните null для vale в хэш-карте.

2) теперь если вы снова столкнетесь с той же charactter

удалите этот элемент из linkedlist, используя адрес, хранящийся в хэш-карте, и теперь вам нужно обновить для элемента, который был после удаленного элемента, предыдущий элемент для него. Сделайте его равным предыдущему элементу элемента deleted.

Анализ Сложности

LinkedlIst добавить элемент - > O (1)

LinkedlIst удалить элемент - > O (1)

HashMap - > O(1)

пробел O (1)

pass - > one in O (n)

    #include<bits/stdc++.h>
    using namespace std;

    typedef struct node
    {
        char ch;
        node *next;
    }node;




    char firstNotRepeatingCharacter(string &s)
    {
      char ans = '_';
      map<char,node*> mp;//hash map atmost may consume O(26) space
      node *head = NULL;//linkedlist atmost may consume O(26) space
      node *last;// to append at last in O(1)
      node *temp1 = NULL;
      node *temp2 = new node[1];
      temp2->ch = '$';
      temp2->next = NULL;
      //This is my one pass of array//
      for(int i = 0;i < s.size();++i)
      {
        //first occurence of character//
        if(mp.find(s[i]) == mp.end())
        {
          node *temp = new node[1];
          temp->ch = s[i];
          temp->next = NULL;
          if(head == NULL)
          {
            head = temp;
            last = temp;
            mp.insert(make_pair(s[i],temp1));
          }
          else
          {
            last->next = temp;
            mp.insert(make_pair(s[i],last));
            last = temp; 
          }
        }
        //Repeated occurence//
        else
        {
          node *temp = mp[s[i]];
          if(mp[s[i]] != temp2)
          {
            if(temp == temp1)
            {
              head = head->next;
              if((head)!=NULL){mp[head->ch] = temp1;}
              else last = head;
              mp[s[i]] = temp2;
            }
            else if((temp->next) != NULL)
            {
              temp->next = temp->next->next;
              if((temp->next) != NULL){mp[temp->next->ch] = temp;}
              else last = temp;
              mp[s[i]] = temp2;
            }
            else
            {
              ;
            }
         }
        }
      if(head == NULL){;}
      else {ans = head->ch;}
      return ans;
    }

    int main()
    {
      int T;
      cin >> T;
      while(T--)
      {
      string str;
      cin >> str;
      cout << str << " ->  " << firstNotRepeatingCharacter(str)<< endl;
      }
      return 0;
    }

требуется только одно сканирование.

использует deque (сохраняет char) и hashmap (сохраняет char->node). При повторении char, получить узел char в deque с помощью hashmap и удалить его из deque (в O(1) время), но сохранить char в hashmap с нулевым значением узла. peek() дает 1-ый уникальный характер.

[pseudocode]
char? findFirstUniqueChar(s):
    if s == null:
        throw
    deque<char>() dq = new
    hashmap<char, node<char>> chToNodeMap = new
    for i = 0, i < s.length(), i++:
        ch = s[i]
        if !chToNodeMap.hasKey(ch):
            chToNodeMap[ch] = dq.enqueue(ch)
        else:
            chNode = chToNodeMap[ch]
            if chNode != null:
                dq.removeNode(chNode)
                chToNodeMap[ch] = null
    if dq.isEmpty():
        return null
    return dq.peek()

// deque interface
deque<T>:
    node<T> enqueue(T t)
    bool removeNode(node<T> n)
    T peek()
    bool isEmpty()

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

вот пример в golang:

package main

import (
    "fmt"
)

func firstNotRepeatingCharacter(s string) int {

    counts := make([]int, 256)
    first := make([]int, 256)

    // The string is parsed only once 
    for i := len(s) - 1; i >= 0; i-- {
        counts[s[i]]++
        first[s[i]] = i
    }

    min := 0
    minValue := len(s) + 1

    // Now we are parsing counts and first slices
    for i := 0; i < 256; i++ {
        if counts[i] == 1 && first[i] < minValue {
            minValue = first[i]
            min = i
        }
    }

    return min

}

func main() {

    fmt.Println(string(firstNotRepeatingCharacter("fff")))
    fmt.Println(string(firstNotRepeatingCharacter("aabbc")))
    fmt.Println(string(firstNotRepeatingCharacter("cbbc")))
    fmt.Println(string(firstNotRepeatingCharacter("cbabc")))

}

go playground


вопрос: найти первый не повторяющийся символ или первый уникальный символ:

сам код понятен.

public class uniqueCharacter1 {
    public static void main(String[] args) {
        String a = "GiniGinaProtijayi";

        firstUniqCharindex(a);
    }

    public static void firstUniqCharindex(String a) {
        int count[] = new int[256];
        for (char ch : a.toCharArray()) {
            count[ch]++;
        } // for

        for (int i = 0; i < a.length(); i++) {
            char ch = a.charAt(i);
            if (count[ch] == 1) {
                System.out.println(i);// 8
                System.out.println(a.charAt(i));// p
                break;
            }
        }

    }// end1
}

В Python:

def firstUniqChar(a):
  count = [0] * 256
  for i in a: count[ord(i)] += 1 
  element = ""
  for items in a:
      if(count[ord(items) ] == 1):
          element = items ;
          break
  return element


a = "GiniGinaProtijayi";
print(firstUniqChar(a)) # output is P