E - 看板の連結 / Concatenation of Signboards 解説 by admin
Claude 4.6 Opus (Thinking)Overview
When connecting \(N\) signs in order, we want to maximally overlap adjacent signs where the “end of the previous sign” matches the “beginning of the next sign,” and find the total number of characters in the entire banner.
Analysis
Key Insight
For two adjacent strings \(S_{i-1}\) and \(S_i\), we want to find the maximum \(l\) such that the last \(l\) characters of \(S_{i-1}\) match the first \(l\) characters of \(S_i\). In other words, this is the problem of finding the length of the longest string that is both a suffix of \(S_{i-1}\) and a prefix of \(S_i\).
Issues with the Naive Approach
If we simply try all values of \(l\) and compare strings, it takes \(O(\min(|S_{i-1}|, |S_i|)^2)\) for each pair, which results in TLE when the strings are long.
Solution
We use the KMP (Knuth-Morris-Pratt) algorithm. By running KMP matching with \(S_i\) as the “pattern” and \(S_{i-1}\) as the “text,” the match length at the point when we finish scanning to the end of \(S_{i-1}\) corresponds to the desired maximum overlap length.
Algorithm
Computing Maximum Overlap Using KMP
Building the failure function: Compute the KMP failure function for \(S_i\) (the pattern). The failure function \(\text{fail}[j]\) represents “the length of the longest proper prefix of the first \(j+1\) characters of \(S_i\) that is also a suffix.”
Running KMP matching: Scan \(S_{i-1}\) (the text) from left to right, advancing the matching with \(S_i\). The match length \(k\) at the point when we reach the end of the text is the longest length where the end of \(S_{i-1}\) matches the beginning of \(S_i\).
Handling constraints:
- If \(k = |S_i|\) (complete match), this violates the constraint \(l < |S_i|\), so we fall back to \(k = \text{fail}[k-1]\).
- If \(k \geq |S_{i-1}|\), this violates the constraint \(l < |S_{i-1}|\), so we similarly fall back using the failure function.
Concrete Example
For \(S_1 = \) abcabc, \(S_2 = \) abcxyz:
- Build the failure function for \(S_2\):
[0, 0, 0, 0, 0, 0] - Run KMP matching on \(S_1\):
a→ \(k=1\),b→ \(k=2\),c→ \(k=3\),a→ \(k=1\) (mismatch withx, fall back and re-match)…- In practice, the last 3 characters
abcofabcabcmatch the firstabcof \(S_2\), giving \(k=3\)
- \(L_2 = 3\), total characters \(= 6 + (6 - 3) = 9\)
Computing Total Characters
\[\text{Total characters} = |S_1| + \sum_{i=2}^{N} (|S_i| - L_i)\]
Complexity
- Time complexity: \(O\left(\sum_{i=1}^{N} |S_i|\right)\)
- For each pair \((S_{i-1}, S_i)\), building the KMP failure function takes \(O(|S_i|)\) and matching takes \(O(|S_{i-1}|)\). Overall, each string is used at most twice (once as text and once as pattern), so the total is proportional to the sum of string lengths.
- Space complexity: \(O\left(\max_i |S_i|\right)\) (for the failure function array)
Implementation Notes
Handling complete matches: If the pattern is completely matched during KMP matching (\(k = |S_i|\)), due to the constraint \(l < |S_i|\), we need to fall back to the next longest match length using \(k = \text{fail}[k-1]\).
Constraint \(l < |S_{i-1}|\): After matching is complete, if \(k \geq |S_{i-1}|\), we also need to fall back using the failure function to find the maximum value satisfying the constraint.
Fast input: In Python, use
sys.stdin.buffer.read()to read all input at once, reducing I/O overhead.Source Code
import sys
def compute_kmp_failure(pattern):
m = len(pattern)
fail = [0] * m
k = 0
for i in range(1, m):
while k > 0 and pattern[k] != pattern[i]:
k = fail[k - 1]
if pattern[k] == pattern[i]:
k += 1
fail[i] = k
return fail
def find_max_overlap(s_prev, s_curr):
# We want the longest l such that:
# 1 <= l < len(s_prev) and l < len(s_curr)
# s_prev ends with s_curr[:l]
# We use KMP: treat s_curr as "pattern" and match against s_prev
# But we only care about the suffix of s_prev matching a prefix of s_curr.
# Build KMP failure function for s_curr
# Then run KMP matching of s_curr against s_prev, and at the end of s_prev,
# the current match length gives us the longest prefix of s_curr that matches
# a suffix of s_prev.
len_prev = len(s_prev)
len_curr = len(s_curr)
if len_prev == 0 or len_curr == 0:
return 0
# Build failure function for s_curr
fail = compute_kmp_failure(s_curr)
# Run KMP: match s_curr (as pattern) against s_prev (as text)
k = 0 # number of characters of s_curr matched so far
for i in range(len_prev):
while k > 0 and s_curr[k] != s_prev[i]:
k = fail[k - 1]
if s_curr[k] == s_prev[i]:
k += 1
if k == len_curr:
# Full match of s_curr found in s_prev; we need to back off
# because l < len_curr is required
k = fail[k - 1]
# k is the length of the longest prefix of s_curr that matches
# a suffix of s_prev. But we also need l < len_prev.
# k < len_curr is guaranteed (we handled full match above).
# We need k < len_prev as well.
while k >= len_prev:
if k == 0:
break
k = fail[k - 1]
return k
def main():
input_data = sys.stdin.buffer.read().decode()
tokens = input_data.split()
idx = 0
N = int(tokens[idx]); idx += 1
strings = []
for i in range(N):
strings.append(tokens[idx]); idx += 1
total = len(strings[0])
for i in range(1, N):
overlap = find_max_overlap(strings[i-1], strings[i])
total += len(strings[i]) - overlap
print(total)
if __name__ == '__main__':
main()
This editorial was generated by claude4.6opus-thinking.
投稿日時:
最終更新: