Skip to content

Commit d3ae444

Browse files
committed
Created 834._Sum_of_Distances_in_Tree.md
1 parent 1e56ad7 commit d3ae444

File tree

2 files changed

+313
-0
lines changed

2 files changed

+313
-0
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
### 828. Unique Letter String
2+
3+
4+
5+
题目:
6+
https://leetcode.com/problems/unique-letter-string
7+
8+
难度:
9+
Hard
10+
11+
题意:
12+
13+
1. 求一个字符串中的所有非空子字符串,这些字符串当中的唯一字符数的总和
14+
2. 唯一字符数就是在字符串中出现只有一次的字符的个数
15+
16+
思路:
17+
18+
- 枚举左右端点,暴力统计子字符串的唯一字符数,复杂度是o(n^3)
19+
- 枚举左端点,然后遍历右端点,每次保存上一次的字符统计个数,和唯一字符数,并更新为当前的数值,复杂度是o(n^2)
20+
- 注意到,当某个字符出现次数大于1个后,后面不管怎么遍历,该字符都不会是唯一字符,因此,有个剪枝动作,如果此时某个字符出现次数大于1个后,后面直接跳过该字符
21+
- 因此我们需要实现把每个字符出现的位置记录下来,以便可以跳过某些字符
22+
- 枚举左端点,然后遍历右端点,加上剪枝之后,每个字符至多被扫描2次。把字符串拆成26个字符后,从左边遍历到右边,就需要一个堆来维持,类似于归并排序的merge操作,这个堆最多26个元素,时间复杂度是o(52nlog26)
23+
- 我们可以另外想一种解决方案。假设我们把26个字母全部拆开,当某个字符第一次出现到第二次出现中间,它就能贡献一个唯一字符。假设A这个字符,第一次出现是位置1,第二次出现是位置10,于是1-10中间就可以增加一个唯一字符,同理,其他字符也是这样的。假设左端点移动,所需要改变的,也只有一个字符而已,这个字符的第一次出现的位置,到第二次出现的位置,改变了位置。我们不要想着每个字符串有多少唯一字符,而是从总体来看,每个字符贡献了多少次唯一字符。还是上面那个例子,A这个字符,第一次出现是位置1,第二次出现是位置10,那么贡献了10-1=9次唯一字符。扫描左端点时,只需要不断用上一个左端点字符移动来更新总唯一字符数即可。复杂度是o(n)
24+
25+
解法一:
26+
27+
```java
28+
class Solution {
29+
private class Item implements Comparable<Item> {
30+
int alph;
31+
int idx;
32+
int pos;
33+
34+
public Item(int alph, int idx, int pos) {
35+
this.alph = alph;
36+
this.idx = idx;
37+
this.pos = pos;
38+
}
39+
40+
@Override
41+
public int compareTo(Item o) {
42+
return Integer.compare(pos, o.pos);
43+
}
44+
}
45+
46+
private int solve(List[] idxList, int start, int len, int[] init) {
47+
PriorityQueue<Item> queue = new PriorityQueue<Item>();
48+
boolean[] flag = new boolean[26];
49+
for (int i = 0;i < 26;i++) {
50+
flag[i] = false;
51+
}
52+
int ret = 0;
53+
int res = 0;
54+
int pre = start - 1;
55+
56+
for (int i = 0;i < 26;i++) {
57+
int idx = init[i];
58+
if (idx != idxList[i].size()) {
59+
queue.add(new Item(i, idx, (Integer) idxList[i].get(idx)));
60+
}
61+
}
62+
63+
while (!queue.isEmpty()) {
64+
Item top = queue.poll();
65+
ret += (long) res * (top.pos - pre) % MOD;
66+
if (ret >= MOD) {
67+
ret -= MOD;
68+
}
69+
if (flag[top.alph]) {
70+
res--;
71+
} else {
72+
flag[top.alph] = true;
73+
if (top.idx + 1 != idxList[top.alph].size()) {
74+
queue.add(new Item(top.alph, top.idx + 1, (Integer) idxList[top.alph].get(top.idx + 1)));
75+
}
76+
res++;
77+
}
78+
pre = top.pos;
79+
}
80+
81+
ret += (long) res * (len - pre) % MOD;
82+
if (ret >= MOD) {
83+
ret -= MOD;
84+
}
85+
return ret;
86+
}
87+
88+
private static int MOD = 1000000007;
89+
90+
public int uniqueLetterString(String S) {
91+
List[] idxList = new List[26];
92+
int[] init = new int[26];
93+
for (int i = 0;i < 26;i++) {
94+
idxList[i] = new ArrayList<Integer>();
95+
init[i] = 0;
96+
}
97+
for (int i = 0;i < S.length();i++) {
98+
idxList[S.charAt(i) - 'A'].add(i);
99+
}
100+
101+
int ret = 0;
102+
for (int i = 0;i < S.length();i++) {
103+
ret += solve(idxList, i, S.length(), init);
104+
if (ret >= MOD) {
105+
ret -= MOD;
106+
}
107+
init[S.charAt(i) - 'A']++;
108+
}
109+
110+
return ret;
111+
}
112+
}
113+
```
114+
115+
解法二:
116+
117+
```java
118+
class Solution {
119+
private static int MOD = 1000000007;
120+
121+
public int uniqueLetterString(String S) {
122+
List<Integer>[] idxList = new List[26];
123+
int[] pos = new int[26];
124+
for (int i = 0;i < 26;i++) {
125+
idxList[i] = new ArrayList<Integer>();
126+
pos[i] = 0;
127+
}
128+
for (int i = 0;i < S.length();i++) {
129+
idxList[S.charAt(i) - 'A'].add(i);
130+
}
131+
132+
int ret = 0;
133+
int res = 0;
134+
for (int i = 0;i < 26;i++) {
135+
if (pos[i] < idxList[i].size()) {
136+
if (pos[i] + 1 < idxList[i].size()) {
137+
res += idxList[i].get(pos[i] + 1) - idxList[i].get(pos[i]);
138+
} else {
139+
res += S.length() - idxList[i].get(pos[i]);
140+
}
141+
}
142+
}
143+
144+
for (int i = 0;i < S.length();i++) {
145+
ret += res;
146+
if (ret >= MOD) {
147+
ret -= MOD;
148+
}
149+
int j = S.charAt(i) - 'A';
150+
if (pos[j] + 1 < idxList[j].size()) {
151+
res -= idxList[j].get(pos[j] + 1) - idxList[j].get(pos[j]);
152+
} else {
153+
res -= S.length() - idxList[j].get(pos[j]);
154+
}
155+
pos[j]++;
156+
if (pos[j] < idxList[j].size()) {
157+
if (pos[j] + 1 < idxList[j].size()) {
158+
res += idxList[j].get(pos[j] + 1) - idxList[j].get(pos[j]);
159+
} else {
160+
res += S.length() - idxList[j].get(pos[j]);
161+
}
162+
}
163+
}
164+
165+
return ret;
166+
}
167+
}
168+
```
169+
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
### 834. Sum of Distances in Tree
2+
3+
4+
5+
题目:
6+
https://leetcode.com/problems/sum-of-distances-in-tree/
7+
8+
难度:
9+
Hard
10+
11+
题意:
12+
13+
1. 给定一棵树
14+
2. 问每个节点到其他节点的距离之和是多少
15+
3. N <= 10000
16+
17+
思路:
18+
19+
- 最简单的方法,假设我们要求节点0与其他节点的距离之和,只需要一遍dfs即可,其他节点同理。复杂度是o(n^2)
20+
21+
- 注意到,树中每个边都是一个桥(就是去掉这个边图就变成非连通了),树中的任意两个节点都是一个固定的路径。当我们计算不同节点的dfs,其实是有一部分路径是重复计算的(自己拿笔画一下)
22+
23+
- 来看看分析的思路。从问题出发,要问每个节点到其他节点的距离和,想象这个点A是树根(每个点都可以是树根),那么我们遍历一下所有连接这个节点的边。想象一下某个边e连接的是一个子树,点A跟这个子树所有的点都得经过边e,因此我们把问题下推,计算这个子树的树根与其他节点的距离之和,然后加上这个子树的数量就等于点A跟这个子树所有的点的距离和
24+
25+
- 有个点需要注意的,由于边是无向的,我们动态规划的方向也需要两个方向,怎么理解呢。比如说,假设A和B连着。以A为树根计算子问题的时候,顺序是A->B,以B为树根计算子问题的时候,顺序是B->A,相当于对边进行动态规划,规划两个方向
26+
27+
- 解法知道了,就需要编码。注意到,N <= 10000,如果递归的话,小心stack over flow。需要自己模拟栈
28+
29+
30+
代码:
31+
32+
```java
33+
class Solution {
34+
private class Edge {
35+
private int x;
36+
private int y;
37+
private int num;
38+
private int dist;
39+
40+
public Edge(int x, int y, int num, int dist) {
41+
this.x = x;
42+
this.y = y;
43+
this.num = num;
44+
this.dist = dist;
45+
}
46+
47+
public int getX() {
48+
return x;
49+
}
50+
51+
public void setX(int x) {
52+
this.x = x;
53+
}
54+
55+
public int getY() {
56+
return y;
57+
}
58+
59+
public void setY(int y) {
60+
this.y = y;
61+
}
62+
63+
public int getNum() {
64+
return num;
65+
}
66+
67+
public void setNum(int num) {
68+
this.num = num;
69+
}
70+
71+
public int getDist() {
72+
return dist;
73+
}
74+
75+
public void setDist(int dist) {
76+
this.dist = dist;
77+
}
78+
}
79+
80+
private void solve(Edge edge, List[] edgeList) {
81+
if (edge.num != -1) {
82+
return;
83+
}
84+
85+
LinkedList<Edge> queue = new LinkedList<Edge>();
86+
Stack<Edge> stack = new Stack<Edge>();
87+
88+
queue.addLast(edge);
89+
stack.add(edge);
90+
91+
while (!queue.isEmpty()) {
92+
Edge e = queue.pollFirst();
93+
for (Edge next: (List<Edge>) edgeList[e.y]) {
94+
if (next.y == e.x) {
95+
continue;
96+
}
97+
if (next.num != -1) {
98+
continue;
99+
}
100+
queue.addLast(next);
101+
stack.add(next);
102+
}
103+
}
104+
105+
while (!stack.empty()) {
106+
Edge e = stack.pop();
107+
e.num = e.dist = 0;
108+
for (Edge next: (List<Edge>) edgeList[e.y]) {
109+
if (next.y == e.x) {
110+
continue;
111+
}
112+
e.num += next.num;
113+
e.dist += next.num + next.dist;
114+
}
115+
e.num ++;
116+
}
117+
}
118+
119+
public int[] sumOfDistancesInTree(int N, int[][] edges) {
120+
List[] edgeList = new List[N];
121+
for (int i = 0;i < edgeList.length;i++) {
122+
edgeList[i] = new ArrayList<Edge>();
123+
}
124+
125+
for (int i = 0;i < edges.length;i++) {
126+
edgeList[edges[i][0]].add(new Edge(edges[i][0], edges[i][1], -1, -1));
127+
edgeList[edges[i][1]].add(new Edge(edges[i][1], edges[i][0], -1, -1));
128+
}
129+
130+
int[] result = new int[N];
131+
for (int i = 0;i < N;i++) {
132+
result[i] = 0;
133+
for (Edge next: (List<Edge>) edgeList[i]) {
134+
if (next.dist == -1) {
135+
solve(next, edgeList);
136+
}
137+
result[i] += next.dist + next.num;
138+
}
139+
}
140+
return result;
141+
}
142+
}
143+
```
144+

0 commit comments

Comments
 (0)