Редакция для Игра «Виселица»


Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.
Submitting an official solution before solving the problem yourself is a bannable offence.

Автор: montes332

1. Идея

Нужно аккуратно смоделировать игру «Виселица» ровно по правилам из условия.

Есть:

  • загаданное слово word,
  • число жизней lives,
  • последовательность ходов moves.

На каждом ходе надо понять, что произошло:

  • букву уже называли раньше → REPEAT,
  • буква есть в слове → HIT,
  • буквы нет в слове → MISS, и уменьшаем lives.

После каждого обработанного хода игра может завершиться:

  • если открыто всё слово, то WIN,
  • если жизни закончились, то LOSE.

Если игра завершилась, остальные ходы больше не обрабатываются.


2. Наблюдения

Наблюдение 1. Нужно помнить, какие буквы уже назывались

Так как алфавит состоит только из строчных латинских букв, удобно хранить массив из 26 значений used.

  • used[0] соответствует букве 'a',
  • used[1]'b',
  • ...
  • used[25]'z'.

Это позволяет за O(1) проверять, была ли буква названа раньше.

Наблюдение 2. Состояние слова можно хранить отдельно

Текущее состояние удобно хранить в строке или списке символов длины |word|:

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

Например, если word = "apple", то состояние может меняться так:

  • "_____"
  • "a____"
  • "app__"
  • "apple"

Наблюдение 3. Один ход может открыть сразу несколько позиций

Если буква встречается в слове несколько раз, нужно открыть все её вхождения за один ход.

Поэтому при новом символе c надо пройтись по всему слову и заменить все подходящие '_' на c.

Наблюдение 4. После завершения партии дальнейшие ходы игнорируются

Это важный момент условия. Если уже получен WIN или LOSE, то:

  • не печатаем результаты для оставшихся ходов,
  • не меняем состояние,
  • просто выводим финальное состояние и итог.

3. Алгоритм

  1. Считываем word, lives, q, moves.
  2. Создаём:
    • массив used из 26 значений false,
    • текущее состояние state, заполненное символами '_'.
  3. Заводим флаг finished = false.
  4. Для каждого хода moves[i]:
    • если finished == true, прекращаем обработку;
    • находим номер буквы: id = c - 'a';
    • если used[id] уже истинно:
      • выводим REPEAT;
    • иначе:
      • помечаем букву как использованную;
      • ищем эту букву во всём слове;
      • если нашли хотя бы одно вхождение:
        • открываем все такие позиции;
        • выводим HIT;
      • иначе:
        • уменьшаем lives на 1;
        • выводим MISS.
    • после этого проверяем:
      • если state == word, то партия завершена;
      • если lives == 0, то партия завершена.
  5. После цикла выводим финальное состояние слова.
  6. Определяем итог:
    • если state == word, выводим WIN;
    • иначе если lives == 0, выводим LOSE;
    • иначе выводим PLAY.

4. Почему это работает

Докажем, что алгоритм точно соответствует правилам задачи.

1. Повторные ходы обрабатываются правильно

Для каждой буквы мы запоминаем, называлась ли она раньше, в массиве used.

  • Если буква уже была, алгоритм выводит REPEAT.
  • При этом состояние слова не меняется.
  • Число жизней тоже не меняется.

Это в точности совпадает с условием.

2. Новая буква из слова обрабатывается правильно

Если буква ещё не использовалась, мы помечаем её в used и просматриваем всё слово.

  • Все позиции, где word[j] == c, открываются в state.
  • Если хотя бы одна такая позиция есть, выводим HIT.
  • Жизни при этом не уменьшаются.

Это ровно соответствует правилу для удачного угадывания.

3. Новая буква, которой нет в слове, обрабатывается правильно

Если при просмотре слова не найдено ни одной позиции с буквой c, то:

  • выводим MISS,
  • уменьшаем lives на 1.

Это в точности правило промаха.

4. Завершение игры определяется правильно

После каждого обработанного хода алгоритм проверяет два условия завершения:

  • state == word — значит, все буквы открыты, нужно завершить игру с результатом WIN;
  • lives == 0 — значит, попытки закончились, игра завершается с результатом LOSE.

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

5. Финальный ответ корректен

После завершения обработки:

  • state содержит состояние слова после последнего реально обработанного хода;
  • если слово полностью открыто, выводим WIN;
  • если жизни закончились, выводим LOSE;
  • иначе ходы просто закончились раньше завершения партии, значит выводим PLAY.

Следовательно, алгоритм полностью моделирует игру по условию.


5. Сложность

Пусть:

  • n = |word|,
  • q — число ходов.

Для каждого хода мы в худшем случае один раз просматриваем всё слово, то есть тратим O(n).

Итого:

  • время: O(q * n),
  • память: O(n + 26).

С учётом ограничений n <= 50 и q <= 100 этого более чем достаточно.


6. Код на C++17

#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main() {
    string word;
    cin >> word;

    int lives, q;
    cin >> lives;
    cin >> q;

    string moves;
    cin >> moves;

    vector<bool> used(26, false);
    string state(word.size(), '_');

    bool finished = false;

    for (int i = 0; i < q; i++) {
        if (finished) {
            break;
        }

        char c = moves[i];
        int id = c - 'a';

        if (used[id]) {
            cout << "REPEAT\n";
        } else {
            used[id] = true;
            bool hit = false;

            for (int j = 0; j < (int)word.size(); j++) {
                if (word[j] == c) {
                    state[j] = c;
                    hit = true;
                }
            }

            if (hit) {
                cout << "HIT\n";
            } else {
                lives--;
                cout << "MISS\n";
            }
        }

        if (state == word || lives == 0) {
            finished = true;
        }
    }

    cout << state << "\n";
    if (state == word) {
        cout << "WIN\n";
    } else if (lives == 0) {
        cout << "LOSE\n";
    } else {
        cout << "PLAY\n";
    }

    return 0;
}

7. Код на Python 3

word = input().strip()
lives = int(input())
q = int(input())
moves = input().strip()

used = [False] * 26
state = ['_'] * len(word)

finished = False

for i in range(q):
    if finished:
        break

    c = moves[i]
    idx = ord(c) - ord('a')

    if used[idx]:
        print("REPEAT")
    else:
        used[idx] = True
        hit = False

        for j in range(len(word)):
            if word[j] == c:
                state[j] = c
                hit = True

        if hit:
            print("HIT")
        else:
            lives -= 1
            print("MISS")

    if ''.join(state) == word or lives == 0:
        finished = True

final_state = ''.join(state)
print(final_state)

if final_state == word:
    print("WIN")
elif lives == 0:
    print("LOSE")
else:
    print("PLAY")

Комментарии

Еще нет ни одного комментария.