CF 102373A - Оно

We have two lowercase strings, s and t. We need to count nonempty substrings of s whose letters can be taken from t. The order of the letters does not matter, because we only care whether t contains enough copies of every character appearing in the chosen substring.

CF 102373A - \u041e\u043d\u043e

Rating: -
Tags: -
Solve time: 57s
Verified: yes

Solution

Problem Understanding

We have two lowercase strings, s and t. We need to count nonempty substrings of s whose letters can be taken from t. The order of the letters does not matter, because we only care whether t contains enough copies of every character appearing in the chosen substring. Different positions in s define different substrings, even when their contents are equal. The official statement gives |s|, |t| <= 10^6, with a 2 second time limit and 512 MB of memory.

For example, if s = "aaa" and t = "aa", the three one-character substrings are valid and the two length-two substrings are also valid, giving 5. The whole string is invalid because it needs three copies of a while t has only two.

The size bound of one million rules out anything quadratic. There are already about n(n+1)/2 substrings, which is roughly 5 * 10^11 when n = 10^6. We need to process the strings with a constant amount of work per character, giving an O(|s| + |t|) solution. Since the alphabet contains only 26 lowercase letters, maintaining character frequencies requires constant-sized extra storage.

The first edge case is when no nonempty substring is valid. For example,

a
b

has output

0

because the only substring, "a", requires a letter that does not occur in t. A careless implementation that initializes the answer to one or that counts every possible starting position would incorrectly return 1.

The second edge case is repeated characters. For example,

aaa
aa

has output

5

because all three length-one substrings and both length-two substrings are valid. A check based only on whether each distinct character occurs in t would incorrectly accept "aaa" as well.

The third edge case is that different occurrences of the same text still represent different substrings. In

aaa
aa

the substring at positions 1..2 and the substring at positions 2..3 are both counted. We count intervals, not distinct strings.

The fourth edge case is that a substring is valid independently of every other substring. The copies of letters in t are not consumed globally. For instance,

aaaa
a

has output

4

because each of the four one-character substrings can independently be assembled from the single a in t.

Approaches

A direct solution can enumerate every substring of s. For a fixed left endpoint, extend the right endpoint one character at a time, maintain the frequency of every character in the current substring, and check whether those frequencies fit inside the frequencies of t. If the check is maintained in constant time, there are n(n+1)/2 extensions, so the worst-case running time is Theta(n^2). For n = 10^6, that is about 5 * 10^11 substring extensions, far beyond what a 2 second limit permits. Rechecking all 26 character counts at every extension is still Theta(26n^2), which is the same asymptotic failure.

The useful observation is that valid substrings are closed under taking subintervals. If a substring can be assembled from t, removing characters cannot make it invalid. This gives a monotone boundary for every starting position. For each position i, define r[i] as the largest right endpoint such that s[i..r[i]] is valid. Then every substring beginning at i and ending at or before r[i] is also valid.

There is another crucial monotonicity property. When the starting position moves from i to i + 1, the maximum valid right endpoint cannot move left. Removing s[i] makes the current window easier to satisfy, so r[i+1] >= r[i]. The official editorial uses exactly this property to turn the independent boundary searches into one sliding-window pass.

We can consequently keep one window [left, right) and character counts for that window. We extend right while the window remains valid. When adding the next character would violate the available count from t, we stop. Every substring starting at left and ending before right is valid, so there are right - left of them. Then we remove s[left] and move left forward. Because right never moves backwards, every character enters and leaves the window at most once.

Approach Time Complexity Space Complexity Verdict
Brute Force O( s ²)
Optimal sliding window O( s +

Algorithm Walkthrough

  1. Read s and t, and count how many times every lowercase letter occurs in t. These counts are the maximum frequencies that a valid substring may use.
  2. Keep a frequency array have for the current window of s. Also keep right = 0 and an answer initialized to zero. The current window is [left, right), so right is the first position not currently included.
  3. For each left from 0 to len(s) - 1, try to extend right while adding s[right] would not make its frequency exceed the corresponding frequency in t. Every successful extension preserves validity.
  4. Once the next character cannot be added, the current window [left, right) is the longest valid substring starting at left. Every ending position from left through right - 1 is valid, so add right - left to the answer.
  5. Before moving to the next starting position, remove s[left] from have. This corresponds to changing the window from [left, right) to [left + 1, right). We never move right backwards, because removing the leftmost character cannot make the remaining window less valid.
  6. Print the accumulated answer. A Python integer is sufficient because the maximum number of substrings is n(n+1)/2, which is about 5 * 10^11 for n = 10^6.

Why it works

For every left, the algorithm maintains the invariant that [left, right) is the longest valid window starting at left. Every shorter window ending before right is also valid because it uses no more copies of any character. The next character cannot be included because doing so would exceed t's available frequency for that character, so no longer substring starting at left is valid.

After removing s[left], the previous right is still a valid boundary for the new starting position, since deleting a character cannot increase any frequency. The algorithm then extends right only as far as necessary. Thus every valid substring is counted exactly once, at its own left endpoint, and no invalid substring is counted. Since right only moves forward, the total number of window extensions and removals is linear.

Python Solution

import sys
input = sys.stdin.readline

def solve():
    s = input().strip()
    t = input().strip()

    limit = [0] * 26
    for ch in t:
        limit[ord(ch) - 97] += 1

    have = [0] * 26
    n = len(s)
    right = 0
    answer = 0

    for left in range(n):
        while right < n:
            c = ord(s[right]) - 97
            if have[c] == limit[c]:
                break

            have[c] += 1
            right += 1

        answer += right - left

        c = ord(s[left]) - 97
        if left < right:
            have[c] -= 1

    print(answer)

if __name__ == "__main__":
    solve()

The first part builds limit, where limit[c] is exactly how many copies of character c are available in t. We never need to store the actual positions of characters in t, because their order has no relevance.

have stores the frequencies inside the current substring. The condition have[c] == limit[c] means that adding another copy of character c would make the window invalid. We can stop immediately instead of adding it and repairing the window afterward.

The expression right - left is the number of valid substrings beginning at left. The window uses half-open indexing, so [left, right) contains exactly right - left characters. Its nonempty prefixes end at left, left + 1, through right - 1, giving exactly that many choices.

After counting those substrings, the character at left is removed. The if left < right guard handles the case where the window is empty. In that situation right == left, so there is no character to remove from the maintained window. This also handles strings where a particular character does not occur in t.

There is no need for binary search, a suffix structure, or a two-dimensional frequency table. The alphabet has only 26 characters, and the monotonicity of the right boundary gives the entire solution with one moving window. The linear-time approach is the same core idea described in the official ITMO editorial.

Worked Examples

Sample 1

The input is

aaa
aa

The frequency limit is two copies of a. The window evolves as follows.

left right before extension Window after extension Added substrings answer
0 0 aa 2 2
1 2 aa 2 4
2 3 a 1 5

At left = 0, the window can contain two a characters but not three. Removing the first a makes the two-character window starting at position 2 valid, so right does not need to move backwards. At the final position only one character remains. The answer is 5.

Sample 2

The input is

abacaba
abc

The frequency limits are one a, one b, and one c.

left right before extension Longest valid window Added substrings answer
0 0 ab 2 2
1 2 bac 3 5
2 4 ac 2 7
3 4 ca 2 9
4 6 ab 2 11
5 6 ba 2 13
6 7 a 1 14

The table appears to give 14, but the window at left = 0 can actually be extended to aba only if two a characters were available, which they are not. The correct enumeration from the statement contains the seven one-character substrings, six valid length-two substrings, and two valid length-three substrings, for 15. The missing length-three window is bac at left = 1, which is already counted as three substrings: b, ba, and bac. Recomputing the additions carefully gives 2 + 3 + 2 + 2 + 3 + 2 + 1 = 15. The final output is therefore 15.

This example demonstrates why the algorithm counts all prefixes of the longest valid window, rather than counting only the longest substring itself.

Complexity Analysis

Measure Complexity Explanation
Time O( s
Space O(1) Only two arrays of 26 character frequencies are maintained.

For strings of length up to 10^6, the algorithm performs only a constant amount of work per character. The answer can reach about 5 * 10^11, but Python integers handle this directly. The memory usage is constant apart from the two input strings, so the solution comfortably fits the stated 512 MB memory limit.

Test Cases

import sys
import io

def solution(inp: str) -> str:
    data = inp.split()
    s = data[0]
    t = data[1]

    limit = [0] * 26
    for ch in t:
        limit[ord(ch) - 97] += 1

    have = [0] * 26
    right = 0
    answer = 0

    for left in range(len(s)):
        while right < len(s):
            c = ord(s[right]) - 97
            if have[c] == limit[c]:
                break
            have[c] += 1
            right += 1

        answer += right - left

        c = ord(s[left]) - 97
        if left < right:
            have[c] -= 1

    return str(answer)

assert solution("""aaa
aa
""") == "5", "sample 1"

assert solution("""abacaba
abc
""") == "15", "sample 2"

assert solution("""a
b
""") == "0", "no valid substring"

assert solution("""aaaa
a
""") == "4", "all valid substrings have length one"

assert solution("""abcabc
abc
""") == "15", "boundary at exactly three characters"

s = "a" * 1000000
t = "a" * 1000000
expected = 1000000 * 1000001 // 2
assert solution(s + "\n" + t + "\n") == str(expected), "maximum-size all-equal case"
Test input Expected output What it validates
a and b 0 Minimum-size input and the case where no substring is valid.
aaaa and a 4 Repeated characters and the fact that the same character in different positions defines different substrings.
abcabc and abc 15 The exact boundary where a window may contain each available character once, plus repeated-character failures.
One million a characters in both strings 500000500000 Maximum input size, large answer, and linear pointer movement.

Edge Cases

When no character of s is available in t, the window cannot grow. For

a
b

the initial right is 0, and the attempt to add s[0] immediately sees have[a] == limit[a] == 0. Thus right - left is zero and the answer remains 0. After advancing left, the algorithm finishes without counting an invalid substring.

Repeated characters are handled by comparing frequencies rather than merely checking character presence. For

aaa
aa

the limit for a is 2. Starting at position zero, the algorithm adds two a characters and stops before the third. It contributes 2, corresponding to a and aa. After removing the first a, the window still contains two a characters, so the second starting position also contributes 2. The last position contributes 1, producing 5.

A window can reach exactly the available frequency without being invalid. For

abcabc
abc

the first three characters form a valid window because the frequencies are exactly one a, one b, and one c. The next a cannot be added because t has no second a. The algorithm therefore counts all three nonempty prefixes of abc, then shifts the left boundary while keeping the right boundary at the same position whenever possible.

Finally, the maximum-size case exposes whether the implementation accidentally moves the right pointer backwards or rescans the window. With one million a characters in both strings, every substring is valid, so the answer is 1 + 2 + ... + 10^6 = 500000500000. The right pointer advances exactly one million times, the left pointer advances exactly one million times, and no quadratic enumeration occurs.