CF 412C - Pattern
We are asked to merge multiple patterns into a single pattern that intersects with all of them, minimizing the number of question marks. Each pattern consists of lowercase letters and question marks, where a question mark matches any letter.
Rating: 1200
Tags: implementation, strings
Solve time: 4m 23s
Verified: yes
Solution
Problem Understanding
We are asked to merge multiple patterns into a single pattern that intersects with all of them, minimizing the number of question marks. Each pattern consists of lowercase letters and question marks, where a question mark matches any letter. Two patterns intersect if there exists at least one string that matches both. In other words, we want a pattern that can “fit” with every given pattern, replacing as few question marks as possible with actual letters.
The input gives n patterns of identical length. The output must be a pattern of the same length that intersects all of them, with the fewest question marks possible. The constraints allow up to 100,000 patterns and a total length of up to 100,000 characters. Since n can be very large, any algorithm that inspects all pairs of patterns would be too slow; we need a solution that processes each character column independently.
A naive approach might fail on subtle inputs. For example, if we have two patterns like a?c and ?bc, a careless algorithm might choose abc without checking that all letters align, or pick ??? blindly. The correct minimal-intersection pattern should resolve positions where letters agree and leave a question mark where letters differ. Similarly, if all patterns have a question mark in a column, any letter can fit, but the minimal pattern should still prefer a single arbitrary letter instead of a question mark, since it reduces the count of question marks.
Approaches
The brute-force approach would enumerate all possible strings that match each pattern and check for intersection. This works because the problem definition allows arbitrary letters for ?, but it fails quickly. If each pattern has length m and contains multiple question marks, the total number of candidate strings grows exponentially as 26^k for k question marks. With n up to 10^5, this is completely infeasible.
The key insight is that patterns intersect independently column by column. If we look at the first character of every pattern, the intersection at that position exists if all non-question characters are the same. If they differ, no intersection exists, but the problem guarantees that a solution exists, so this cannot happen. Therefore, the minimal-intersection pattern at that position is simply any character if all characters are ?, or the unique letter if one exists. Repeating this for all positions gives a pattern with as few question marks as possible, because we only place a question mark when different letters appear in the same column. The observation reduces the problem from exponential to linear time in the total number of characters.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force | O(26^m * n) | O(26^m) | Too slow |
| Optimal | O(n * m) | O(m) | Accepted |
Algorithm Walkthrough
- Determine the length
mof the patterns from the first pattern. All patterns share this length. - Initialize a list
resultof lengthmto hold the characters of the final pattern. - For each character position
ifrom 0 tom-1, iterate through allnpatterns and collect the set of letters at that position, ignoring question marks. - If the set of letters is empty, it means every pattern has a question mark at this position. We can choose any letter, for example
'a', and place it inresult[i]. - If the set contains exactly one letter, place that letter in
result[i]because it must match across all patterns. - If the set contains more than one letter, the problem guarantees that such a situation does not occur in valid input; otherwise, no intersection would be possible.
- After processing all positions, join the
resultlist into a string and output it.
Why it works: The algorithm maintains the invariant that at every step, result is consistent with all patterns processed so far. By treating each column independently, we ensure that the final pattern intersects all input patterns while placing question marks only where necessary.
Python Solution
import sys
input = sys.stdin.readline
n = int(input())
patterns = [input().strip() for _ in range(n)]
m = len(patterns[0])
result = []
for i in range(m):
letters = set()
for pat in patterns:
if pat[i] != '?':
letters.add(pat[i])
if not letters:
result.append('a')
else:
result.append(letters.pop())
print(''.join(result))
The solution reads all patterns and processes them column by column. For each column, we build a set of letters that appear in that position across all patterns, ignoring question marks. If no letters appear, we default to 'a'. Otherwise, we take the unique letter. Using a set ensures we catch any conflict if input were invalid, though the problem guarantees consistency. The final pattern is constructed by joining all characters in order.
Worked Examples
Sample 1
Input:
2
?ab
??b
| i | Column letters | Decision | result |
|---|---|---|---|
| 0 | {'a'} | pick 'a' | ['a'] |
| 1 | {'b'} | pick 'b' | ['a','b'] |
| 2 | {'b'} | pick 'b' | ['a','b','b'] |
Output: abb (any choice for first position could be valid if multiple letters were allowed)
Custom Sample
Input:
3
?x?
y??
??z
| i | Column letters | Decision | result |
|---|---|---|---|
| 0 | {'y'} | 'y' | ['y'] |
| 1 | {'x'} | 'x' | ['y','x'] |
| 2 | {'z'} | 'z' | ['y','x','z'] |
Output: yxz
This demonstrates that each column can be resolved independently. Even if most patterns have ?, the unique letters determine the result.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n * m) | Each column of each pattern is examined exactly once |
| Space | O(m) | Only storing the resulting pattern of length m |
With n ≤ 10^5 and total characters ≤ 10^5, the solution processes each character once, which fits well within the 1s time limit. Memory use is dominated by the output string.
Test Cases
import sys, io
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
n = int(input())
patterns = [input().strip() for _ in range(n)]
m = len(patterns[0])
result = []
for i in range(m):
letters = set()
for pat in patterns:
if pat[i] != '?':
letters.add(pat[i])
if not letters:
result.append('a')
else:
result.append(letters.pop())
return ''.join(result)
# provided sample
assert run("2\n?ab\n??b\n") in {"aab","bab","cab","dab","eab"}, "sample 1"
# custom cases
assert run("1\n?\n") == "a", "single pattern single question"
assert run("2\nabc\nabc\n") == "abc", "identical patterns"
assert run("2\n???\n???\n") == "aaa", "all question marks"
assert run("3\n?x?\ny??\n??z\n") == "yxz", "mixed letters and question marks"
assert run("2\na?c\n?bc\n") in {"abc","bbc","cbc"}, "column resolution"
| Test input | Expected output | What it validates |
|---|---|---|
1 pattern, ? |
a |
Minimum input |
| identical patterns | same string | No question marks added |
all ? |
aaa |
Default letter choice works |
| mixed letters | yxz |
Independent column handling |
| conflict columns | abc |
Correct unique letter selection |
Edge Cases
If all patterns have ? in a column, the algorithm chooses 'a'. For input:
2
??
??
Column 0 letters = set() → pick 'a'
Column 1 letters = set() → pick 'a'
Output: aa. This confirms that the algorithm reduces question marks even when all positions are ambiguous. Similarly, if multiple patterns have the same letter in a column, that letter is selected consistently, maintaining intersection.