結果
| 問題 |
No.1192 半部分列
|
| コンテスト | |
| ユーザー |
qwewe
|
| 提出日時 | 2025-05-14 13:02:37 |
| 言語 | C++17 (gcc 13.3.0 + boost 1.87.0) |
| 結果 |
WA
|
| 実行時間 | - |
| コード長 | 8,632 bytes |
| コンパイル時間 | 1,191 ms |
| コンパイル使用メモリ | 112,828 KB |
| 実行使用メモリ | 228,488 KB |
| 最終ジャッジ日時 | 2025-05-14 13:04:21 |
| 合計ジャッジ時間 | 7,193 ms |
|
ジャッジサーバーID (参考情報) |
judge3 / judge2 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 9 WA * 1 TLE * 1 -- * 14 |
ソースコード
#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <unordered_map> // Using unordered_map for visited set for potentially better average performance
#include <algorithm>
#include <functional> // For std::hash
using namespace std;
// Custom hash function for pairs of integers to be used with unordered_map
// This implementation uses a combination method similar to what's found in Boost libraries.
struct pair_hash {
template <class T1, class T2>
std::size_t operator() (const std::pair<T1, T2>& pair) const {
// Hash the first element
auto hash1 = std::hash<T1>{}(pair.first);
// Hash the second element
auto hash2 = std::hash<T2>{}(pair.second);
// Combine the two hashes into a single hash value.
// The constants and bit shifts are common techniques to improve hash distribution.
size_t seed = 0;
// Combine hash1 into seed
seed ^= hash1 + 0x9e3779b9 + (seed << 6) + (seed >> 2);
// Combine hash2 into seed
seed ^= hash2 + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
};
// Function to precompute the next occurrence indices for a given string `s`.
// `next_idx[i][c]` will store the minimum index `j >= i` such that `s[j] == c + 'a'`.
// If character `c` is not found at or after index `i`, it stores `len` (string length).
// This allows O(1) lookup for the next character position.
vector<vector<int>> build_next_char(const string& s, int len) {
// Initialize the table with the sentinel value `len`, indicating "not found".
// Table dimensions: (length + 1) x 26 (for each character 'a' through 'z').
vector<vector<int>> next_idx(len + 1, vector<int>(26, len));
// Iterate backwards through the string.
// This allows propagating the next occurrence information efficiently.
for (int i = len - 1; i >= 0; --i) {
// Copy the 'next occurrence' information from the state corresponding to index `i+1`.
// This handles cases where the character `s[i]` is not the one we're looking for.
for (int c = 0; c < 26; ++c) {
next_idx[i][c] = next_idx[i + 1][c];
}
// Update the entry for the character `s[i]`. The first occurrence at or after `i`
// is at index `i` itself.
next_idx[i][s[i] - 'a'] = i;
}
// The table `next_idx` is now populated.
return next_idx;
}
// State structure to be stored in the priority queue.
// Represents a state in the Dijkstra-like search.
struct State {
int idx_s; // The index in string S reached after matching the last character of `current_w`.
int idx_t; // The index in string T reached after matching the last character of `current_w`.
string current_w; // The subsequence string constructed so far to reach this state.
// Custom comparison operator for the priority queue.
// We want a min-heap based on lexicographical order of `current_w`.
// `std::priority_queue` is a max-heap by default. Using `std::greater<State>`
// reverses the comparison, effectively making it a min-heap based on this operator.
// Standard string comparison (`>`) provides lexicographical comparison.
bool operator>(const State& other) const {
return current_w > other.current_w;
}
};
// Use unordered_map to keep track of visited states (pairs of indices).
// The key is `pair<int, int>` representing (idx_s, idx_t).
// The value is `bool` (true if visited). Using `pair_hash` for hashing the pair keys.
unordered_map<pair<int, int>, bool, pair_hash> visited;
int main() {
// Optimize C++ standard streams I/O operations.
ios_base::sync_with_stdio(false);
cin.tie(NULL);
string s, t;
cin >> s >> t;
int n = s.length();
int m = t.length();
// Precompute the next character occurrence tables for both strings S and T.
vector<vector<int>> next_s = build_next_char(s, n);
vector<vector<int>> next_t = build_next_char(t, m);
// Initialize the priority queue. It stores `State` objects and orders them
// using the custom comparator (`operator>`) via `std::greater<State>`, ensuring
// the state with the lexicographically smallest string is always at the top.
priority_queue<State, vector<State>, greater<State>> pq;
// Push the initial state onto the priority queue.
// Indices -1 indicate we are "before the start" of both strings.
// The initial subsequence string is empty.
pq.push({-1, -1, ""});
// Main loop of the Dijkstra-like search. Continues as long as there are states to explore.
while (!pq.empty()) {
// Retrieve the state with the lexicographically smallest string from the top of the queue.
State current = pq.top();
pq.pop();
int idx_s = current.idx_s;
int idx_t = current.idx_t;
// Create a pair representing the current state's indices.
pair<int, int> state_pair = {idx_s, idx_t};
// Check if this state (defined by the pair of indices) has already been visited.
// The `visited` map ensures we process each `(idx_s, idx_t)` pair only once,
// specifically when reached via the lexicographically smallest path (string).
if (visited.count(state_pair)) {
continue; // If visited, skip this state; a better path was already found.
}
// Mark the current state as visited.
visited[state_pair] = true;
// Try appending each character 'a' through 'z' to the current subsequence string.
for (char c_char = 'a'; c_char <= 'z'; ++c_char) {
int c = c_char - 'a'; // Convert character to integer index 0-25.
// Find the index of the next occurrence of character `c` in string S, after `idx_s`.
int next_idx_s_val;
if (idx_s == -1) { // If at the start, look from index 0.
next_idx_s_val = next_s[0][c];
} else { // Otherwise, look from the index immediately after `idx_s`.
next_idx_s_val = next_s[idx_s + 1][c];
}
// If `next_idx_s_val == n`, character `c` was not found in S after `idx_s`.
// Cannot extend with this character. Continue to the next character.
if (next_idx_s_val == n) {
continue;
}
// Find the index of the next occurrence of character `c` in string T, after `idx_t`.
int next_idx_t_val;
if (idx_t == -1) { // If at the start, look from index 0.
next_idx_t_val = next_t[0][c];
} else { // Otherwise, look from the index immediately after `idx_t`.
next_idx_t_val = next_t[idx_t + 1][c];
}
// Check if character `c` was found in S but NOT found in T (index is sentinel `m`).
if (next_idx_t_val == m) {
// We found a string `current.current_w + c_char` that satisfies the conditions:
// 1. It's a subsequence of S (because `next_idx_s_val` was valid).
// 2. It's NOT a subsequence of T (because `next_idx_t_val` was sentinel `m`).
// Since Dijkstra explores states in lexicographical order of the path string,
// this must be the lexicographically smallest such string.
// Print the resulting string and terminate the program.
cout << current.current_w + c_char << "\n";
return 0;
}
// If character `c` was found in both S and T.
// The next state corresponds to the pair of indices `(next_idx_s_val, next_idx_t_val)`.
pair<int, int> next_state_pair = {next_idx_s_val, next_idx_t_val};
// Check if this next state `(next_idx_s_val, next_idx_t_val)` has been visited.
if (!visited.count(next_state_pair)) {
// If not visited, push the new state onto the priority queue.
// The new state includes the updated indices and the extended string.
// Note: `current.current_w + c_char` creates a new string object.
// This string copying might be a performance bottleneck for very long strings/paths.
pq.push({next_idx_s_val, next_idx_t_val, current.current_w + c_char});
}
}
}
// If the loop finishes without finding a solution (priority queue becomes empty),
// it means every subsequence of S is also a subsequence of T.
// In this case, output -1 as required.
cout << -1 << "\n";
return 0;
}
qwewe