給定詞典單詞和句子,句子可被翻譯當且僅當能分割成詞典單詞。求每個句子最長可翻譯前綴長度。
struct TrieNode {
TrieNode* child[26];
bool is_end;
TrieNode() : is_end(false) { memset(child, 0, sizeof(child)); }
};
#include <bits/stdc++.h>
using namespace std;
struct TrieNode {
int child[26];
bool is_end;
TrieNode() : is_end(false) { memset(child, -1, sizeof(child)); }
};
vector<TrieNode> trie(1); // 0 為根
void insert(const string& s) {
int u = 0;
for (char ch : s) {
int c = ch - 'a';
if (trie[u].child[c] == -1) {
trie[u].child[c] = trie.size();
trie.emplace_back();
}
u = trie[u].child[c];
}
trie[u].is_end = true;
}
int solve(const string& s) {
int n = s.size();
vector<bool> dp(n + 1, false);
dp[0] = true;
int ans = 0;
for (int i = 0; i < n; ++i) {
if (!dp[i]) continue;
ans = i; // 更新可達最遠位置
int u = 0;
for (int j = i; j < n; ++j) {
int c = s[j] - 'a';
if (trie[u].child[c] == -1) break;
u = trie[u].child[c];
if (trie[u].is_end) dp[j + 1] = true;
}
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int W, C; cin >> W >> C;
string word;
for (int i = 0; i < W; ++i) {
cin >> word;
insert(word);
}
string sentence;
for (int i = 0; i < C; ++i) {
cin >> sentence;
cout << solve(sentence) << "\n";
}
return 0;
}
| 優化 | 效果 |
|---|---|
| Trie 節點用數組+索引 | 避免指針開銷,緩存友好 |
| DP 早停 | `if (!dp[i]) continue` 跳過不可達位置 |
| 匹配早停 | Trie 無對應分支立即 break |
| 單詞結尾標記 | 只在單詞結尾更新 DP,非結尾不更新 |
AC 自動機適合多模式匹配「找出所有出現位置」。本題只需「從每個可達位置向前匹配」,Trie 直接遍歷更簡單、常數更小。題目註釋說明 Trie 甚至比 AC 更快。
此題是 Trie 樹結合 DP 解決「單詞拆分/可翻譯前綴」問題的教科書級範例,展示了字典樹在字符串分割問題中的核心優勢。