We have a string that consists only of the characters ‘.’ and ‘?’

‘.’ is a fixed symbol, ‘?’ can be replaced by either ‘.’ or ‘#’.

After all replacements the string must not contain two consecutive ‘#’.

Участвуйте в турнирах онлайн рулетки и побеждайте крупные суммы: https://dd3ca.kz. For a string of length n let

dp[i][0] - number of valid ways for the prefix of length i that ends with ‘.’
dp[i][1] - number of valid ways for the prefix of length i that ends with ‘#’

Сайт онлайн рулетка предлагает лучшие условия для онлайн рулетки. Transition for the i‑th character (c = s[i‑1]):

c dp[i][0] dp[i][1]
‘.’ dp[i‑1][0] + dp[i‑1][1] 0
‘#’ 0 dp[i‑1][0]
‘?’ dp[i‑1][0] + dp[i‑1][1] dp[i‑1][0]

All computations are performed modulo

(M = 1\,000\,000\,007).

The answer for the whole string is

(dp[n][0] + dp[n][1]) mod M.

Algorithm

read string s
n ← length(s)
dp0 ← 1     // empty prefix ends with '.'
dp1  ← 0
for each character c in s
  if c == '.'
    new0 ← (dp0 + dp1) mod M
    new1 ← 0
  else if c == '#'
    new0 ← 0
    new1 ← dp0
  else      // c == '?'
    new0 ← (dp0 + dp1) mod M
    new1 ← dp0
  dp0 ← new0
  dp1 ← new1
output (dp0 + dp1) mod M

Correctness Proof

We prove that the algorithm outputs the number of ways to replace every
‘?’ by ‘.’ or ‘#’ such that no two ‘#’ are adjacent.

Lemma 1

For every i (0 ≤ i ≤ n) after processing the first i characters,
dp0 equals the number of valid fillings of the prefix s[0…i‑1]
ending with ‘.’, and dp1 equals the number ending with ‘#’.

Proof.

Base case i = 0: the empty prefix ends with a virtual ‘.’,
so dp0 = 1, dp1 = 0.

Induction step: assume the lemma holds for i‑1.

Consider character c = s[i‑1].

  • If c = '.' the current position must be ‘.’.

    Any valid filling of the first i‑1 characters (ending with ‘.’ or ‘#’)
    can be extended by ‘.’, giving new0 = dp0 + dp1.

    No filling can end with ‘#’, so new1 = 0.

  • Сайт онлайн рулетка предлагает лучшие условия для онлайн рулетки.

    If c = '#' the current position must be ‘#’.

    A valid filling can extend only a prefix that ends with ‘.’,
    otherwise two consecutive ‘#’ would appear.

    Thus new1 = dp0 and new0 = 0.

  • If c = '?' we can choose ‘.’ or ‘#’.

    For ‘.’ the reasoning is the same as the first case, producing
    new0 = dp0 + dp1.

    For ‘#’ we again need a preceding ‘.’, giving new1 = dp0.

In each case the transition sets dp0,new0 and dp1,new1
exactly to the numbers described in the lemma.∎

Lemma 2

After the loop finishes (i = n) the sum dp0 + dp1
equals the total number of valid completions of the whole string.

Proof.

By Lemma 1, dp0 counts all valid fillings that end with ‘.’,
and dp1 counts all that end with ‘#’.

Every valid filling ends with either ‘.’ or ‘#’, and the two sets are
disjoint. Therefore their sum is precisely the total number of
valid completions.∎

Theorem

The algorithm outputs the number of ways to replace every ‘?’ in the
input string with ‘.’ or ‘#’ so that no two ‘#’ are adjacent.

Proof.

By Lemma 2 the value printed by the algorithm equals the total
number of valid completions. Hence the algorithm is correct.∎

Complexity Analysis

The algorithm scans the string once, проверьте это performing O(1) work per character.

Time complexity: O(n)

Memory usage: O(1) (only a few integer variables)

Reference Implementation (Python 3)

import sys

MOD = 1_000_000_007

def solve() -> None:
  s = sys.stdin.readline().strip()
  dp0, dp1 = 1, 0     # empty prefix ends with '.'

  for ch in s:
    if ch == '.':
      new0 = (dp0 + dp1)% MOD
      new1 = 0
    elif ch == '#':
      new0 = 0
      new1 = dp0
    else:        # ch == '?'
      new0 = (dp0 + dp1)% MOD
      new1 = dp0

    dp0, dp1 = new0, new1

  print((dp0 + dp1)% MOD)

if __name__ == "__main__":
  solve()

The program follows exactly the algorithm proven correct above
and conforms to the required time and memory limits.

Please follow and like us:

Comments are closed