AtCoder

ABC329 E問題(Stamp)を解く

AtCoder_ABC329_E

AtCoder が提供しているABC(AtCoder Beginner Contest)329 のE問題をC++とPythonで解いてみました。ABC329は、2023年11月18日21:00に実施されました。

AtCoder の紹介はこちらに、プログラミングの方針はこちらに記事があります。

E問題 Stamp(Difficulty : 1539)

問題はリンク先をご覧ください。

ABC329 E問題 Stamp

キューを使って、文字列を処理していきます。AtCoder Problems による Difficulty は 1539 でした。

解答案

この問題は、Mの制約が5以下と小さいため解くことができます。基本的な考え方は、以下となります。

操作を以下とします。

  • 文字列SのM文字の部分文字列S’が以下の文字列になっているか確認する。
    • S’の j 文字目(0 ≦ j < M)がTの j 文字目と一致している または ‘#’ になっている。
  • この場合、S’の文字をすべて’#’に置き換える。

上記の操作を繰り返して、最終的にSの文字がすべて ‘#’ になっていれば、解答は “Yes” となります。もともとの文字が残っていれば “No” となります。最初、Sは ‘#’ を含んでいないため、SがTを1か所も含んでいないと “No” となります。

文字列を ‘#’ に入れ替えた後、文字列S全体として、置き換えた前後 M-1 文字だけしか影響を受けません。このため、新しく調べる範囲は、(M-1)×2 文字しか増えません。また、1回あたりの確認は、M回のループが必要となるため、全体としての計算量は、$O(N M^2)$ となります。前述のように M が小さいため間に合います。

C++ プログラム例(ABC329E)

置き換えた文字の近傍を置き換えるため、処理する文字の位置をキューに格納して処理します。

操作の前半の確認を関数 check として分けて実装しました(9ー26行目)。すでに確認済の場所であれば、すぐに戻ります(11-13行目、メモ化)。条件を満たす場合にキューに格納します。

最初に、文字列Sで文字列Tに一致している場所をキューに格納します(34ー36行目)。次にキューから場所を取り出して、文字列Sを必要なだけ ‘#’ に入れ替えて、その前後を確認します。

以下が、C++プログラムとなります。

#include <bits/stdc++.h>
using namespace std;

int n, m;
string s, t;
vector<bool> checked;
queue <int> que;

void check(int i)
{
	if (checked[i]) {
		return;
	}

	bool result = true;
	for (int j = 0; j < m; ++j) {
		if ((s[i + j] != t[j])&&(s[i + j] != '#')) {
			result = false;
			break;
		}
	}
	if (result) {
		que.push(i);
		checked[i] = true;
	}
}

int main()
{
	cin >> n >> m;
	cin >> s >> t;
	checked.assign(n - m + 1, false);

	for (int i = 0; i < n - m + 1; ++i) {
		check(i);
	}

	while (!que.empty()) {
		int now = que.front();
		que.pop();
		for (int j = 0; j < m; ++j) {
			s[now + j] = '#';
		}
		for (int i = max(now - m + 1, 0); i < min(now + m, n - m + 1); ++i) {
			check(i);
		}
	}

	bool result = true;
	for (int i = 0; i < n; ++i) {
		if (s[i] != '#') {
			result = false;
		}
	}

	if (result) {
		cout << "Yes" << endl;
	} else {
		cout << "No" << endl;
	}

	return 0;
}

AC(Accepted=正しいプログラム)と判定されました。

Python プログラム例(ABC329E)

キューは、collections.deque を使いました(2、25行目)。Python の文字列は書き換えができないため、リスト化しています(21行目)。処理の基本的な考え方は同じです。以下となります。

"""AtCoder Beginner Contest 329 E"""
from collections import deque


def check(i):
    global m, s, t, checked, que

    if checked[i]:
        return

    result = True
    for j in range(m):
        if s[i + j] != t[j] and s[i + j] != '#':
            result = False
    if result:
        que.append(i)
        checked[i] = True


n, m = map(int, input().split())
s = list(input())
t = input()

checked = [False] * (n - m + 1)
que = deque()
for i in range(n - m + 1):
    check(i)

while que:
    now = que.popleft()
    for j in range(m):
        s[now + j] = '#'
    for i in range(max(now - m + 1, 0), min(now + m, n - m + 1)):
        check(i)

result = True
for i in range(n):
    if s[i] != '#':
        result = False

print("Yes" if result else "No")

こちらも「AC」と判定されました。

最後に

コンテスト中には解くことができませんでした。コンテスト後に公式解説を読み、理解したことを記事にしました。

引き続き ABC の問題を紹介していきます。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA