Каковы хорошие практики ускорения написания кода? - PullRequest
1 голос
/ 25 апреля 2020

Постановка задачи: Шерлок и действительная строка

Этот код проходит все тесты на корректность, 15/20, однако некоторые тесты не пройдены из-за временных ограничений. Каковы хорошие практики для ускорения кода? Как можно избежать for петель?

static String isValid(String s) {

    String yesOrNo;
    //Step 1: count the frequency of each char and out in a map <char, number>
    Map<Character, Integer> map = new HashMap<>();
    for (int i = 0; i < s.length(); i++) {
        int count = 0;
        for (int c = 0; c < s.length(); c++) {
            if (s.charAt(c) == s.charAt(i))
                count++;
        }
        map.put(s.charAt(i), count);
    }
    //Step2: add all the numbers of occurrences of each char into a list
    List<Integer> values = new ArrayList<>();
    for (Map.Entry<Character, Integer> kv : map.entrySet()
    ) {
        values.add(kv.getValue());
    }
    //Step 3: find the benchmark number
    Map<Integer, Integer> occurPairs = new TreeMap<>();
    for (int i = 0; i < values.size(); i++) {
        occurPairs.put(values.get(i), Collections.frequency(values, values.get(i)));
    }
    Map.Entry<Integer, Integer> popVal = Collections.max(occurPairs.entrySet(), Map.Entry.comparingByValue());
    Map.Entry<Integer, Integer> smallest = Collections.min(occurPairs.entrySet(), Map.Entry.comparingByValue());

    //Step 4: compare each value with the benchmark
    int numOfWrong = 0;
    for (Integer value : values) {
        if (!value.equals(popVal.getKey()))
            numOfWrong += Math.abs(popVal.getKey() - value);
    }
    if (occurPairs.size() == 2 && smallest.getValue() == 1 && smallest.getKey() == 1)
        yesOrNo = "YES";
    else if (numOfWrong > 1)
        yesOrNo = "NO";
    else
        yesOrNo = "YES";
    System.out.println(yesOrNo);
    return yesOrNo;
}

Ответы [ 3 ]

3 голосов
/ 25 апреля 2020

Я не скажу вам прямо, что не так, но я дам вам концептуальную основу, с помощью которой вы можете анализировать свой код.

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

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

  • Однако, если вы выполните n операций на букву, тогда вы получите quadrati c масштабирование с или 3n² или всего операций. (Константа впереди не важна. Важен показатель.) Допустим, у вас есть 1000 писем. 3n масштабирование будет означать 3 тысячи операций. 3n² масштабирование будет означать 3 миллион .

Quadrati c в основном означает "не масштабируется". Вместо масштабирования пропорционально длине ввода алгоритмы quadrati c взрываются, когда ввод становится большим. Они нормально работают при нормальной рабочей нагрузке, но распадаются под давлением. Hacker Rank, скорее всего, выдает действительно длинные входные строки в ваш алгоритм, чтобы обнаружить квадратичный c выброс.

Я говорил о n выше. В Java lin go n is s.length(). Можете ли вы определить шаг квадрата c в своем коде, где вы выполняете s.length() * s.length() операции?

Да, на шаге 1 я дважды повторяюсь над s для подсчета частоты каждый символ.

Это верно. Хорошо. Теперь, как вы могли бы выполнить шаг 1 за один проход?

Подумайте, как бы вы сделали это на бумаге. Вы бы не сканировали строку снова и снова, снова и снова, не так ли? Вы просто просматриваете каждую букву один раз и ведете счетчик всех букв, как go. Вероятно, у вас есть таблица с буквами и метками, такими как:

A   ||||
B   
C   |
D   ||
E   |||||||
F   
...

Сделайте то же самое в коде, и это уменьшит до n .

1 голос
/ 25 апреля 2020

В дополнение к ответу @JohnKugelman, вот что вы можете сделать.

Прежде всего, вы проходите свою строку дважды (с внутренним l oop), чтобы подсчитать вхождения, которые равны O ( п ^ 2). Вы уже нашли O (n) решение для этого

Map<Character, Integer> occurences = new HashMap<>();

s.chars()
 .forEach(e-> occurences.put((char)e, occurences.getOrDefault((char)e, 0) + 1));

Теперь нам нужно найти простую итерацию, чтобы найти ответ.

Вот что мы знаем о "YES" падежи.

  • Все буквы имеют одинаковую частоту, например: aabbccddeeff
  • Все буквы имеют одинаковую частоту, но одна буква встречается 1 раз больше. Например: aabb ccddd
  • Все буквы имеют одинаковую частоту, но одну букву с 1 вхождением. Например: aaaabbbbcccce

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

Сначала давайте возьмем наш итератор

Iterator<Map.Entry<Character, Integer>> iterator = occurences
                .entrySet()
                .iterator();

Затем выберите первое число в качестве «эталона» и определите переменную и число для первого различного значения

int benchmark = iterator.next().getValue();
int benchmarkCount = 1;
int firstDifferent = -1;
int differentCount = 0;

Перебирайте числа

while(iterator.hasNext()) {
    int next = iterator.next().getValue();
    if (next == benchmark) { // if the next number is same
        benchmarkCount++; // just update our count
    } else { // if it is different
        // if we haven't found a different one yet or it is the same different value from earlier
        if (firstDifferent == -1 || firstDifferent == next) {
            firstDifferent = next;
            differentCount++;
        }
    }
}

Теперь все мы нужно проанализировать наши цифры

int size = occurences.size();

if (benchmarkCount == size) return "YES"; // if all of the numbers are the same
if (benchmarkCount == size - 1) { // if we hit only single different
    // either the diffent number is 1 or it is greater than our benchmark by value of 1
    if (firstDifferent == 1 || firstDifferent - benchmark == 1) {
        return "YES";
    }
}
// same case with above
if (differentCount == size - 1) {
    if (benchmark == 1 || benchmark - firstDifferent == 1) {
        return "YES";
    }
}

полное решение

static String isValid(String s) {
    Map<Character, Integer> occurences = new HashMap<>();

    s.chars().forEach(e-> occurences.put((char)e, occurences.getOrDefault((char)e, 0) + 1));

    Iterator<Map.Entry<Character, Integer>> iterator = occurences
            .entrySet()
            .iterator();

    int benchmark = iterator.next().getValue();
    int benchmarkCount = 1;
    int firstDifferent = -1;
    int differentCount = 0;

    while(iterator.hasNext()) {
        int next = iterator.next().getValue();
        if (next == benchmark) {
            benchmarkCount++;
        } else {
            if (firstDifferent == -1 || firstDifferent == next) {
                firstDifferent = next;
                differentCount++;
            }
        }
    }
    int size = occurences.size();

    if (benchmarkCount == size) return "YES";
    if (benchmarkCount == size - 1) {
        if (firstDifferent == 1 || firstDifferent - benchmark == 1) {
            return "YES";
        }
    }
    if (differentCount == size - 1) {
        if (benchmark == 1 || benchmark - firstDifferent== 1) {
            return "YES";
        }
    }

    return "NO";
}
0 голосов
/ 27 апреля 2020

Я считаю, что другие решения слишком сложны. Используя количество различных символов (counts.length ниже) и их минимальную частоту (min ниже), мы знаем, что длина строки составляет не менее counts.length * min.

Когда один символ встречается один раз больше, чем min, у нас есть строка длиннее на единицу.

Когда таких символов больше, строка становится длиннее counts.length * min + 1. Когда какой-либо символ встречается даже чаще, чем min + 1, строка тоже становится длиннее counts.length * min + 1.

Как отметил Буньямин Коскунер, в моем решении пропущены такие случаи, как "aabb c". Это работает, но это не так просто, как хотелось:

static String isValid(String s) {
    if (s.isEmpty()) return "YES";
    final int[] counts = s.chars()
            .mapToObj(c -> c)
            .collect(Collectors.groupingBy(c -> c, Collectors.counting()))
            .values()
            .stream()
            .mapToInt(n -> n.intValue())
            .toArray();
    final int min = Arrays.stream(counts).min().getAsInt();
    // Accounts for strings like "aabb" and "aabbb".
    if (s.length() <= counts.length * min + 1) return "YES";
    // Here, strings can be only valid when the minimum is one, like for "aabbc".
    if (min != 1) return "NO";
    final int minButOne = Arrays.stream(counts).filter(n -> n>1).min().getAsInt();
    return s.length() == (counts.length - 1) * minButOne + 1 ? "YES" : "NO";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...