Роберт

Backend-разработчик
Пишу о программировании, музыке, книгах и жизни

Главная

Алгоритм определяющий уникальность символов в строке

Алгоритм определяющий уникальность символов в строке

Буду решать задачи из книги Г. Лакмана Макдауэлла "Карьера программиста" на языке Go.
Итак, первая задача из раздела 1 "Массивы и строки".

1.1. Реализуйте алгоритм, определяющий, все ли символы в строке встречаются только один раз. А если при этом запрещено использование дополнительных структур данных?

1 способ: итеративный метод. Перебираем все символы в строке сравнивая их последовательно друг с другом. При нахождении первого совпадения завершаем поиск. Временная сложность O(n^2), пространственная сложность O(1)

2 способ. Если можно изменять строку, то возможно отсортировать строку и итеративным способом сравнивать сосесние символы. Временная сложность сортировки O(nlogn). Как правило сортировка очень затратная операция.

3 способ заключается в использовании структуры данных, такой как хэш-таблица, которая будет хранить логическое значение, где флаг с ASCII-кодом символа, означает, содержится ли символ алфавита i в строке. Временная сложность O(n), пространственная сложность O(n)

4 способ заключается в использовании бит-вектора.

В книге Донованa А. и Кернигана Б. Язык программирования Go рассмотрен пример битового вектора. Данная структура хорошо подходит для множества небольших положительных чисел над которым часто проводят операции объединения, пересечения, разность и д.р.

Так как у нас строка, будем использовать ASCII-коды символов.

В следующем коде мы предполагаем, что в строке есть только символы в нижнем регистре a-z. Это позволит нам использовать просто одно значение типа int.

Временная сложность будет O(n), пространственная - O(1)

func BitVector(str string) bool {
	// Тип вектора имеет одно поле. Срез беззнаковых целочисленных значений, каждый бит которых представляет возможный элемент множества
	type IntSet struct {
		words []uint
	}
	var s IntSet

	// Вычисляем размер машинного слова sizeWorld
	// На 32 разрядных платформах sizeWord будет равен 32, на 64 соответственно 64.
	const sizeWord = 32 << (^uint(0) >> 63)

	for i := 0; i < len(str); i++ {
		var charCode = str[i]

		byteIndex, indexInByte := uint(charCode/sizeWord), uint(charCode%sizeWord)
		for byteIndex >= uint(len(s.words)) {
			s.words = append(s.words, 0)
		}

		// Хотим узнать включен ли indexInByte-й бит. Проверим принадлежность множеству s.words[byteIndex]
		var bit = s.words[byteIndex] & (1 << indexInByte)
		if bit > 0 {  // != 0 значит символ с ASCII кодом charCode входит в множество
			return false
		} else {
			// Нужно найти элемент с индексом 1 и выставить ему indexInByte-й бит
			// Операцией ИЛИ выставляем indexInByte-й бит (т.е. включаем нужные биты по маске 1 << indexInByte)
			s.words[byteIndex] |= 1 << indexInByte
		}
	}

	return true
}

Все способы решения данной задачи на языке Go на GitHub

Все задачи из данной книги

Роберт Фатхуллин

Статья Роберт Фатхуллин

Backend Developer