Problem Statement
Given two strings s and t, and an integer k, determine if it's possible to extract two non-overlapping substrings of length k from s such that when concatenated, the resulting string contians t as a contiguous substring.
Approach
-
Problem Analysis: The solution involves checking if
tcan be formed by combining parts of two non-overlappingk-length substrings froms. The challenge is to efficiently find these substrings without resorting to an O(n*m) brute-force approach. -
Key Insight:
- Precompute the earliest starting position in
sfor every prefix oft(up to lengthk). - Precompute the latest starting position in
sfor every suffix oft(up to lengthk). - Use rolling hashes to efficiently compare substrings.
- Precompute the earliest starting position in
-
Algorithm Selection:
- Prefix Matching: For each prefix length
i(1 to min(m, k)), find the first occurrence insending at or before positionk. If not found, extend the search rightwards. - Suffix Matching: Similarly, for each suffix length
i, find the last occurrence insstarting at or after positionn-k+1. If not found, extend the search leftwards. - Check Conditions: For each possible split point in
t(from 1 to m-1), verify if the prefix and suffix can be covered by the two substrings without overlapping. - Edge Case Handling: If
tis entirely contained within one of thek-length substrings, directly return valid positions.
- Prefix Matching: For each prefix length
Solution Code
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
const int MAX = 500010;
const unsigned long long BASE = 233;
unsigned long long base_pow[MAX];
unsigned long long s_hash[MAX], t_hash[MAX];
inline unsigned long long get_substr_hash(unsigned long long *hash_arr, int l, int r) {
return hash_arr[r] - hash_arr[l-1] * base_pow[r - l + 1];
}
int main() {
int n, m, k;
cin >> n >> m >> k;
if (n < 2*k || m > 2*k) {
cout << "No" << endl;
return 0;
}
string s_str, t_str;
cin >> s_str >> t_str;
s_str = " " + s_str;
t_str = " " + t_str;
base_pow[0] = 1;
for (int i = 1; i <= n; i++) {
base_pow[i] = base_pow[i-1] * BASE;
}
for (int i = 1; i <= n; i++) {
s_hash[i] = s_hash[i-1] * BASE + (s_str[i] - 'a' + 1);
}
for (int i = 1; i <= m; i++) {
t_hash[i] = t_hash[i-1] * BASE + (t_str[i] - 'a' + 1);
}
vector<int> prefix_min(m+1, n+1);
vector<int> suffix_max(m+2, 0);
int ptr = k;
for (int len = 1; len <= min(m, k); len++) {
unsigned long long target = t_hash[len];
while (ptr <= n) {
int start = ptr - len + 1;
if (start < 1) {
ptr++;
continue;
}
if (get_substr_hash(s_hash, start, ptr) == target) {
break;
}
ptr++;
}
if (get_substr_hash(s_hash, k - len + 1, k) == target) {
ptr = k;
}
prefix_min[len] = ptr;
}
ptr = n - k + 1;
for (int suffix_len = 1; suffix_len <= min(m, k); suffix_len++) {
int t_start = m - suffix_len + 1;
unsigned long long target = get_substr_hash(t_hash, t_start, m);
while (ptr >= 1) {
if (get_substr_hash(s_hash, ptr, ptr + suffix_len - 1) == target) {
break;
}
ptr--;
}
if (get_substr_hash(s_hash, n - k + 1, n - k + suffix_len) == target) {
ptr = n - k + 1;
}
suffix_max[t_start] = ptr;
}
int ans1 = 0, ans2 = 0;
for (int split = 1; split < m; split++) {
if (split > k || m - split > k) continue;
int prefix_end = prefix_min[split];
int suffix_start = suffix_max[split+1];
if (prefix_end <= n && suffix_start >= 1 && suffix_start > prefix_end) {
ans1 = prefix_end - k + 1;
ans2 = suffix_start;
cout << "Yes" << endl;
cout << ans1 << " " << ans2 << endl;
return 0;
}
}
if (k >= m) {
for (int start = 1; start <= k; start++) {
if (start + m - 1 > n) break;
if (get_substr_hash(s_hash, start, start + m - 1) == t_hash[m]) {
cout << "Yes" << endl;
cout << 1 << " " << k+1 << endl;
return 0;
}
}
for (int start = n - k + 1; start <= n; start++) {
if (start + m - 1 > n) break;
if (get_substr_hash(s_hash, start, start + m - 1) == t_hash[m]) {
cout << "Yes" << endl;
cout << n - 2*k + 1 << " " << n - k + 1 << endl;
return 0;
}
}
}
cout << "No" << endl;
return 0;
}
Explanation
- Initialization: The code starts by validating input constraints (if
sis too short orttoo long). - Hashing Setup: Precomputes base powers for rolling hash and computes hash arrays for both strings.
- Prefix and Suffix Arrays:
prefix_min[i]stores earliest position inswhere the prefix oftof lengthiends.suffix_max[i]stores the latest position inswhere the suffix oftstarting atibegins.
- Matching Check: For each possible split of
tinto a prefix and suffix, checks if there exist non-overlapping substrings inscovering both parts. - Edge Handling: If
tfits entirely with in a singlek-length substring, checks the first and last possible positions ins. - Output: If valid positions are found, outputs "Yes" and the positions; otherwise, outputs "No".