План курса / Все задачи / Алгоритмы и структуры данных
Осадки в виде фрикаделек
легко
# решено
С неба падают фрикадельки, оставляя следы ДНК. Дан массив gene из строчных букв и число k. Найдите долю всех подряд идущих подстрок длины k, в которых все символы различны. Это поможет оценить вероятность новых фрикаделек в ближайшие 24 часа.
Пример 1:
Ввод: gene = "ababcdaa", k = 3
Вывод: 0.5
Объяснение: все непрерывные подстроки длины 3: ["aba", "bab", "abс", "bсd", "сda", "daa"], а подстроки без повторяющихся символов: ["abс", "bсd", "сda"]. Значит ответ 3/6 = 0.5
Пример 2:
Ввод: gene = "ab", k = 5
Вывод: 0
Объяснение: если строка меньше длины 5, то нельзя рассчитать вероятность на данный момент
Пример 3:
Ввод: gene = "ab", k = 1
Вывод: 1
Ограничения:
len(gene) ≥ 0
k ≥ 1
public class Solution {
public static double ChanceOfMeatballPrecipitations(string gene, int k) {
// k - размер плавающего окна
if (gene.Length < k) {
return 0;
}
// ключ: буква, значение: число повторов
var windowGene = new Dictionary<char,int>();
for (int i = 0; i < k; i++) {
char ch = gene[i];
if (windowGene.ContainsKey(ch)) {
windowGene[ch]++;
} else {
windowGene[ch] = 1;
}
}
// result - число окон размером k с уникальными символами
int result = windowGene.Count == k ? 1 : 0;
// на каждом шаге цикла сдвигаем плавающее окно вправо на один
for (int i = k; i < gene.Length; i++) {
// ген, который выходит из плавающего окна
char outChar = gene[i - k];
windowGene[outChar]--;
if (windowGene[outChar] == 0) {
windowGene.Remove(outChar);
}
// ген, который добавляем в плавающее окно
char inChar = gene[i];
if (windowGene.ContainsKey(inChar)) {
windowGene[inChar]++;
} else {
windowGene[inChar] = 1;
}
// обновляем ответ
if (windowGene.Count == k) {
result++;
}
}
// считаем вероятность
return (double) result / (gene.Length - k + 1);
}
}
#include <string>
#include <unordered_map>
using namespace std;
double chanceOfMeatballPrecipitations(string& gene, int k) {
// k - размер плавающего окна
if (gene.size() < k) {
return 0;
}
// ключ: буква, значение: число повторов
unordered_map<char,int> windowGene;
for (int i = 0; i < k; i++) {
windowGene[gene[i]]++;
}
// result - число окон размером k с уникальными символами
int result = (windowGene.size() == (size_t)k);
// на каждом шаге цикла сдвигаем плавающее окно вправо на один
for (int i = k; i < gene.size(); i++) {
// ген, который выходит из плавающего окна
char out = gene[i - k];
windowGene[out]--;
if (windowGene[out] == 0) {
windowGene.erase(out);
}
// ген, который добавляем в плавающее окно
char in = gene[i];
windowGene[in]++;
// обновляем ответ
if (windowGene.size() == (size_t)k) {
result++;
}
}
// считаем вероятность
return static_cast<double>(result) / (gene.size() - k + 1);
}
package main
func chanceOfMeatballPrecipitations(gene string, k int) float64 {
// k - размер плавающего окна
if len(gene) < k {
return 0
}
// ключ: буква, значение: число повторов
windowGene := make(map[rune]int)
for _, ch := range gene[:k] {
windowGene[ch]++
}
// result - число окон размером k с уникальными символами
result := 0
if len(windowGene) == k {
result = 1
}
// на каждом шаге цикла сдвигаем плавающее окно вправо на один
for i := k; i < len(gene); i++ {
// ген, который выходит из плавающего окна
out := rune(gene[i-k])
windowGene[out]--
if windowGene[out] == 0 {
delete(windowGene, out)
}
// ген, который добавляем в плавающее окно
in := rune(gene[i])
windowGene[in]++
// обновляем ответ
if len(windowGene) == k {
result++
}
}
// считаем вероятность
return float64(result) / float64(len(gene)-k+1)
}
import java.util.*;
public class Solution {
public static Double chanceOfMeatballPrecipitations(String gene, int k) {
// k - размер плавающего окна
if (gene.length() < k) {
return 0.0;
}
// ключ: буква, значение: число повторов
Map<Character,Integer> windowGene = new HashMap<>();
for (int i = 0; i < k; i++) {
char ch = gene.charAt(i);
windowGene.put(ch, windowGene.getOrDefault(ch, 0) + 1);
}
// result - число окон размером k с уникальными символами
int result = windowGene.size() == k ? 1 : 0;
// на каждом шаге цикла сдвигаем плавающее окно вправо на один
for (int i = k; i < gene.length(); i++) {
// ген, который выходит из плавающего окна
char out = gene.charAt(i - k);
windowGene.put(out, windowGene.get(out) - 1);
if (windowGene.get(out) == 0) {
windowGene.remove(out);
}
// ген, который добавляем в плавающее окно
char in = gene.charAt(i);
windowGene.put(in, windowGene.getOrDefault(in, 0) + 1);
// обновляем ответ
if (windowGene.size() == k) {
result++;
}
}
// считаем вероятность, явное приведение к float
return result / (double)(gene.length() - k + 1);
}
}
from typing import *
from collections import defaultdict
def chance_of_meatball_precipitations(gene: str, k: int) -> float:
# k - размер плавающего окна
if len(gene) < k:
return float(0)
# ключ: буква, значение: число повторов
windowGene = defaultdict(int)
for i in range(k):
windowGene[gene[i]] += 1
# result - число окон размером k с уникальными символами
# обновляем ответ если самое первое плавающее окно содержит k уникальных символа
result = int(len(windowGene) == k)
# на каждом шаге цикла сдвигаем плавающее окно вправо на один
for i in range(k, len(gene)):
# gene[i - k] - ген, который выходит из плаваюшего окна
windowGene[gene[i - k]] -= 1
if windowGene[gene[i - k]] == 0:
del windowGene[gene[i - k]]
# gene[i] - ген, который добавляем в плавающее окно
windowGene[gene[i]] += 1
# обновляем ответ
result += int(len(windowGene) == k)
# считаем вероятность
return float(result) / (len(gene) - k + 1)
/**
* @param {string} gene
* @param {number} k
* @return {number}
*/
export function chanceOfMeatballPrecipitations(gene, k) {
// k - размер плавающего окна
if (gene.length < k) {
return 0;
}
// ключ: буква, значение: число повторов
const windowGene = new Map();
for (let i = 0; i < k; i++) {
windowGene.set(gene[i], (windowGene.get(gene[i]) || 0) + 1);
}
// result - число окон размером k с уникальными символами
let result = windowGene.size === k ? 1 : 0;
// на каждом шаге цикла сдвигаем плавающее окно вправо на один
for (let i = k; i < gene.length; i++) {
// ген, который выходит из плавающего окна
const out = gene[i - k];
const countOut = windowGene.get(out) - 1;
if (countOut === 0) {
windowGene.delete(out);
} else {
windowGene.set(out, countOut);
}
// ген, который добавляем в плавающее окно
const inChar = gene[i];
windowGene.set(inChar, (windowGene.get(inChar) || 0) + 1);
// обновляем ответ
if (windowGene.size === k) {
result++;
}
}
// считаем вероятность
return result / (gene.length - k + 1);
}
Оценка сложности
Время: O(n), где n - длина gene
Память: O(1), потому что дополнительная память тратится только на windowGene, а по условию у нас только английские буквы, а значит размер ограничен и константен