题目## 题目
求和树转换算法
解题思路
-
基本步骤:
- 根据前序和中序遍历重建原始二叉树
- 将原始二叉树转换为求和树
- 输出求和树的中序遍历
-
关键点:
- 满二叉树的特点:每个非叶子节点都有两个子节点
- 求和树节点值 = 左子树所有节点值之和 + 右子树所有节点值之和
- 叶子节点的求和树值为0
代码实现
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
// 树节点定义
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 构建二叉树
TreeNode* buildTree(vector<int>& preorder, int preStart, int preEnd,
vector<int>& inorder, int inStart, int inEnd) {
if (preStart > preEnd) return NULL;
TreeNode* root = new TreeNode(preorder[preStart]);
// 在中序遍历中找根节点位置
int rootIndex = inStart;
for (int i = inStart; i <= inEnd; i++) {
if (inorder[i] == preorder[preStart]) {
rootIndex = i;
break;
}
}
int leftSubtreeSize = rootIndex - inStart;
// 递归构建左右子树
root->left = buildTree(preorder, preStart + 1, preStart + leftSubtreeSize,
inorder, inStart, rootIndex - 1);
root->right = buildTree(preorder, preStart + leftSubtreeSize + 1, preEnd,
inorder, rootIndex + 1, inEnd);
return root;
}
// 转换为求和树
int convertToSumTree(TreeNode* root) {
if (root == NULL) return 0;
int oldValue = root->val;
// 递归计算左右子树的和
int leftSum = convertToSumTree(root->left);
int rightSum = convertToSumTree(root->right);
// 更新当前节点的值为左右子树的和
root->val = leftSum + rightSum;
// 返回包含当前节点的子树所有节点值的和
return oldValue + leftSum + rightSum;
}
// 中序遍历
void inorderTraversal(TreeNode* root, vector<int>& result) {
if (root == NULL) return;
inorderTraversal(root->left, result);
result.push_back(root->val);
inorderTraversal(root->right, result);
}
int main() {
string line;
// 读取前序遍历
getline(cin, line);
istringstream iss1(line);
vector<int> preorder;
int num;
while (iss1 >> num) {
preorder.push_back(num);
}
// 读取中序遍历
getline(cin, line);
istringstream iss2(line);
vector<int> inorder;
while (iss2 >> num) {
inorder.push_back(num);
}
// 构建二叉树
TreeNode* root = buildTree(preorder, 0, preorder.size() - 1,
inorder, 0, inorder.size() - 1);
// 转换为求和树
convertToSumTree(root);
// 获取求和树的中序遍历
vector<int> result;
inorderTraversal(root, result);
// 输出结果
for (int i = 0; i < result.size(); i++) {
cout << result[i];
if (i < result.size() - 1) cout << " ";
}
cout << endl;
return 0;
}
import java.util.*;
public class Main {
static class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 读取前序遍历
String[] preStr = sc.nextLine().split(" ");
int[] preorder = new int[preStr.length];
for(int i = 0; i < preStr.length; i++) {
preorder[i] = Integer.parseInt(preStr[i]);
}
// 读取中序遍历
String[] inStr = sc.nextLine().split(" ");
int[] inorder = new int[inStr.length];
for(int i = 0; i < inStr.length; i++) {
inorder[i] = Integer.parseInt(inStr[i]);
}
// 重建二叉树
TreeNode root = buildTree(preorder, 0, preorder.length - 1,
inorder, 0, inorder.length - 1);
// 转换为求和树
convertToSumTree(root);
// 输出求和树的中序遍历
List<Integer> result = new ArrayList<>();
inorderTraversal(root, result);
// 打印结果
for(int i = 0; i < result.size(); i++) {
System.out.print(result.get(i));
if(i < result.size() - 1) {
System.out.print(" ");
}
}
}
// 构建二叉树
private static TreeNode buildTree(int[] preorder, int preStart, int preEnd,
int[] inorder, int inStart, int inEnd) {
if(preStart > preEnd) return null;
TreeNode root = new TreeNode(preorder[preStart]);
// 在中序遍历中找到根节点位置
int rootIndex = 0;
for(int i = inStart; i <= inEnd; i++) {
if(inorder[i] == preorder[preStart]) {
rootIndex = i;
break;
}
}
int leftSubtreeSize = rootIndex - inStart;
// 递归构建左右子树
root.left = buildTree(preorder, preStart + 1, preStart + leftSubtreeSize,
inorder, inStart, rootIndex - 1);
root.right = buildTree(preorder, preStart + leftSubtreeSize + 1, preEnd,
inorder, rootIndex + 1, inEnd);
return root;
}
// 转换为求和树
private static int convertToSumTree(TreeNode root) {
if(root == null) return 0;
// 获取原始值
int oldValue = root.val;
// 递归计算左右子树的和
int leftSum = convertToSumTree(root.left);
int rightSum = convertToSumTree(root.right);
// 更新当前节点的值为左右子树的和
root.val = leftSum + rightSum;
// 返回包含当前节点的子树所有节点值的和
return oldValue + leftSum + rightSum;
}
// 中序遍历
private static void inorderTraversal(TreeNode root, List<Integer> result) {
if(root == null) return;
inorderTraversal(root.left, result);
result.add(root.val);
inorderTraversal(root.right, result);
}
}
class TreeNode:
def __init__(self, val=0):
self.val = val
self.left = None
self.right = None
def buildTree(preorder, preStart, preEnd, inorder, inStart, inEnd):
if preStart > preEnd:
return None
# 创建根节点
root = TreeNode(preorder[preStart])
# 在中序遍历中找到根节点位置
rootIndex = inStart
for i in range(inStart, inEnd + 1):
if inorder[i] == preorder[preStart]:
rootIndex = i
break
leftSubtreeSize = rootIndex - inStart
# 递归构建左右子树
root.left = buildTree(preorder, preStart + 1, preStart + leftSubtreeSize,
inorder, inStart, rootIndex - 1)
root.right = buildTree(preorder, preStart + leftSubtreeSize + 1, preEnd,
inorder, rootIndex + 1, inEnd)
return root
def convertToSumTree(root):
if not root:
return 0
# 保存原始值
oldValue = root.val
# 递归计算左右子树的和
leftSum = convertToSumTree(root.left)
rightSum = convertToSumTree(root.right)
# 更新当前节点的值为左右子树的和
root.val = leftSum + rightSum
# 返回包含当前节点的子树所有节点值的和
return oldValue + leftSum + rightSum
def inorderTraversal(root, result):
if not root:
return
inorderTraversal(root.left, result)
result.append(root.val)
inorderTraversal(root.right, result)
def solve():
# 读取输入
preorder = list(map(int, input().split()))
inorder = list(map(int, input().split()))
# 构建二叉树
root = buildTree(preorder, 0, len(preorder) - 1,
inorder, 0, len(inorder) - 1)
# 转换为求和树
convertToSumTree(root)
# 获取求和树的中序遍历
result = []
inorderTraversal(root, result)
# 输出结果
print(' '.join(map(str, result)))
if __name__ == "__main__":
solve()
算法分析
-
时间复杂度:
- 构建二叉树: O ( n 2 ) \mathcal{O}(n^2) O(n2)
- 转换为求和树: O ( n ) \mathcal{O}(n) O(n)
- 总体: O ( n 2 ) \mathcal{O}(n^2) O(n2)
-
空间复杂度:
- 递归调用栈: O ( h ) \mathcal{O}(h) O(h), h h h为树高
- 存储结果: O ( n ) \mathcal{O}(n) O(n)
- 总体: O ( n ) \mathcal{O}(n) O(n)
解题思路
这是一个字符串匹配和动态规划问题。使用 K M P KMP KMP 算法找到所有模式串的匹配位置,然后用动态规划求解最大不相交子串数量。
关键点:
- 使用 K M P KMP KMP 算法高效查找所有匹配位置
- 用动态规划数组 d p [ i ] dp[i] dp[i] 表示到位置 i i i 的最大不相交子串数量
- 对每个位置,考虑是否选择以该位置结尾的匹配
算法步骤:
- 对每个模式串进行 K M P KMP KMP 匹配
- 记录每个位置结尾的所有可能匹配
- 动态规划求解最优结果
代码
#include <bits/stdc++.h>
using namespace std;
class StringMatcher {
private:
static const int MAXN = 100005;
vector<int> next;
vector<vector<int>> matches;
vector<int> dp;
public:
StringMatcher(int size) {
next.resize(MAXN);
matches.resize(MAXN);
dp.resize(MAXN);
}
void buildNext(const string& pattern) {
int len = pattern.length();
next[0] = -1;
int i = 0, j = -1;
while (i < len) {
if (j == -1 || pattern[i] == pattern[j]) {
i++; j++;
next[i] = j;
} else {
j = next[j];
}
}
}
void kmpMatch(const string& text, const string& pattern) {
buildNext(pattern);
int n = text.length(), m = pattern.length();
int i = 0, j = 0;
while (i < n) {
if (j == -1 || text[i] == pattern[j]) {
i++; j++;
} else {
j = next[j];
}
if (j == m) {
matches[i-1].push_back(i-m-1);
j = next[j];
}
}
}
int solve(const string& text, const vector<string>& patterns) {
int textLen = text.length();
// 对每个模式串进行KMP匹配
for (const string& pattern : patterns) {
kmpMatch(text, pattern);
}
// 动态规划求解
for (int i = 0; i < textLen; i++) {
dp[i+1] = dp[i];
for (int start : matches[i]) {
dp[i+1] = max(dp[i+1], dp[start+1] + 1);
}
}
return dp[textLen];
}
};
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<string> patterns(n);
for (int i = 0; i < n; i++) {
cin >> patterns[i];
}
string text;
cin >> text;
StringMatcher matcher(text.length());
cout << matcher.solve(text, patterns) << endl;
return 0;
}
import java.util.*;
public class Main {
static class StringMatcher {
private static final int MAXN = 100005;
private int[] next;
private List<List<Integer>> matches;
private int[] dp;
public StringMatcher(int size) {
next = new int[MAXN];
matches = new ArrayList<>(MAXN);
dp = new int[MAXN];
for (int i = 0; i < MAXN; i++) {
matches.add(new ArrayList<>());
}
}
private void buildNext(String pattern) {
int len = pattern.length();
next[0] = -1;
int i = 0, j = -1;
while (i < len) {
if (j == -1 || pattern.charAt(i) == pattern.charAt(j)) {
i++; j++;
next[i] = j;
} else {
j = next[j];
}
}
}
private void kmpMatch(String text, String pattern) {
buildNext(pattern);
int n = text.length(), m = pattern.length();
int i = 0, j = 0;
while (i < n) {
if (j == -1 || text.charAt(i) == pattern.charAt(j)) {
i++; j++;
} else {
j = next[j];
}
if (j == m) {
matches.get(i-1).add(i-m-1);
j = next[j];
}
}
}
public int solve(String text, List<String> patterns) {
int textLen = text.length();
for (String pattern : patterns) {
kmpMatch(text, pattern);
}
for (int i = 0; i < textLen; i++) {
dp[i+1] = dp[i];
for (int start : matches.get(i)) {
dp[i+1] = Math.max(dp[i+1], dp[start+1] + 1);
}
}
return dp[textLen];
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
List<String> patterns = new ArrayList<>();
for (int i = 0; i < n; i++) {
patterns.add(sc.next());
}
String text = sc.next();
StringMatcher matcher = new StringMatcher(text.length());
System.out.println(matcher.solve(text, patterns));
sc.close();
}
}
class StringMatcher:
def __init__(self, size):
self.MAXN = 100005
self.next = [-1] * self.MAXN
self.matches = [[] for _ in range(self.MAXN)]
self.dp = [0] * self.MAXN
def build_next(self, pattern):
length = len(pattern)
self.next[0] = -1
i, j = 0, -1
while i < length:
if j == -1 or pattern[i] == pattern[j]:
i += 1
j += 1
self.next[i] = j
else:
j = self.next[j]
def kmp_match(self, text, pattern):
self.build_next(pattern)
n, m = len(text), len(pattern)
i = j = 0
while i < n:
if j == -1 or text[i] == pattern[j]:
i += 1
j += 1
else:
j = self.next[j]
if j == m:
self.matches[i-1].append(i-m-1)
j = self.next[j]
def solve(self, text, patterns):
text_len = len(text)
for pattern in patterns:
self.kmp_match(text, pattern)
for i in range(text_len):
self.dp[i+1] = self.dp[i]
for start in self.matches[i]:
self.dp[i+1] = max(self.dp[i+1], self.dp[start+1] + 1)
return self.dp[text_len]
# 主函数
if __name__ == "__main__":
n = int(input())
patterns = [input() for _ in range(n)]
text = input()
matcher = StringMatcher(len(text))
print(matcher.solve(text, patterns))
算法及复杂度
- 算法: K M P KMP KMP + 动态规划
- 时间复杂度: O ( N M ) \mathcal{O}(NM) O(NM),其中 N N N 是文本长度, M M M 是所有模式串长度之和
- 空间复杂度: O ( N ) \mathcal{O}(N) O(N),用于存储 n e x t next next 数组和匹配位置
8万+

被折叠的 条评论
为什么被折叠?



