Многие ответы пытаются O (n), но забывают о фактических затратах на вставку и удаление из списков / ассоциативных массивов / наборов, которые они используют для отслеживания.
Если вы можете предположить, что символ представляет собой один байт, тогда вы используете простой массив, индексированный символом, и сохраняете в нем счет. Это действительно O (n), потому что доступ к массиву гарантирован O (1), и последний проход по массиву для поиска первого элемента с 1 - это постоянное время (потому что массив имеет небольшой фиксированный размер).
Если вы не можете предположить, что символ представляет собой один байт, я бы предложил отсортировать строку и затем выполнить один проход, проверяя смежные значения. Это будет O (n log n) для сортировки плюс O (n) для последнего прохода. Таким образом, это фактически O (n log n), что лучше, чем O (n ^ 2). Кроме того, он практически не имеет места, что является еще одной проблемой для многих ответов, которые пытаются O (n).