1. 省份数量
有 n
个城市,其中一些彼此相连,另一些没有相连。如果城市 a
与城市 b
直接相连,且城市 b
与城市 c
直接相连,那么城市 a
与城市 c
间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n
的矩阵 isConnected
,其中 isConnected[i][j] = 1
表示第 i
个城市和第 j
个城市直接相连,而 isConnected[i][j] = 0
表示二者不直接相连。
返回矩阵中 省份 的数量。
Example 1
输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
Example 2
输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3
代码
class Solution {
private:
vector<bool> visited;
void dfs(vector<vector<int>> &isConnected, int i) {
visited[i] = true;
for (int next = 0; next < visited.size(); next++) {
if (isConnected[i][next] && !visited[next]) dfs(isConnected, next);
}
}
public:
int findCircleNum(vector<vector<int>> &isConnected) {
visited.assign(isConnected.size(), false);
int ans = 0;
for (int i = 0; i < visited.size(); ++i) {
if (!visited[i]) {
ans++;
dfs(isConnected, i);
}
}
return ans;
}
};
2. 重建序列
请判断原始的序列 org
是否可以从序列集 seqs
中唯一地 重建 。序列 org
是 1 到 n 整数的排列,其中
1
≤
n
≤
1
0
4
1 ≤ n ≤ 10^4
1≤n≤104。
重建 是指在序列集 seqs
中构建最短的公共超序列,即 seqs
中的任意序列都是该最短序列的子序列。
Example 1
输入: org = [1,2,3], seqs = [[1,2],[1,3]]
输出: false
解释:[1,2,3] 不是可以被重建的唯一的序列,因为 [1,3,2] 也是一个合法的序列。
Example 2
输入: org = [1,2,3], seqs = [[1,2]]
输出: false
解释:可以重建的序列只有 [1,2]。
代码
class Solution {
public:
bool sequenceReconstruction(vector<int> &org, vector<vector<int>> &seqs) {
unordered_set<int> st;
for (auto &seq:seqs) {
for (int i:seq) st.emplace(i);
}
int n = org.size();
if (n == 1 && st.count(org[0]) == 0) return false;
if (st.size() != n) return false;
vector<unordered_set<int>> graph(n + 1);
vector<int> indegree(n + 1, 0);
for (auto &seq:seqs) {
int len = seq.size();
for (int i = 0; i < len - 1; ++i) {
int from = seq[i], to = seq[i + 1];
if (graph[from].count(to) == 0) {
graph[from].emplace(to);
++indegree[to];
}
}
}
queue<int> q;
for (int i = 1; i <= n; ++i) {
if (indegree[i] == 0) q.emplace(i);
}
vector<int> ret;
while (!q.empty()) {
if (q.size() > 1) return false;
int cur = q.front();
q.pop();
ret.emplace_back(cur);
for (auto i:graph[cur]) {
--indegree[i];
if (indegree[i] == 0) q.emplace(i);
}
}
return ret == org;
}
};
3. 外星文字典
现有一种使用英语字母的外星文语言,这门语言的字母顺序与英语顺序不同。
给定一个字符串列表 words
,作为这门语言的词典,words
中的字符串已经 按这门新语言的字母顺序进行了排序 。
请你根据该词典还原出此语言中已知的字母顺序,并 按字母递增顺序 排列。若不存在合法字母顺序,返回 ""
。若存在多种可能的合法字母顺序,返回其中 任意一种 顺序即可。
字符串 s
字典顺序小于 字符串 t
有两种情况:
- 在第一个不同字母处,如果
s
中的字母在这门外星语言的字母顺序中位于t
中字母之前,那么s
的字典顺序小于t
。 - 如果前面
min(s.length, t.length)
字母都相同,那么s.length < t.length
时,s
的字典顺序也小于t
。
Example 1
输入:words = ["wrt","wrf","er","ett","rftt"]
输出:"wertf"
Example 2
输入:words = ["z","x"]
输出:"zx"
代码
string alienOrder(vector<string> &words) {
vector<bitset<26>> graph(26, bitset<26>(0));
vector<int> inDegree(26, -1); // -1 表示包含该字母
int totalAlpha = 0;
for (auto &word:words) { // 建图
for (char ch:word) {
inDegree[ch - 'a'] = 0;
}
}
for (int i = 0; i < 26; ++i) {
if (inDegree[i] == 0) totalAlpha++;
}
for (int i = 1, j, len; i < words.size(); ++i) { // 计算邻接表和入度
len = min(words[i - 1].size(), words[i].size());
j = -1;
while (++j < len && words[i - 1][j] == words[i][j]) continue;
if (j == len && words[i - 1].size() > words[i].size()) return {}; // 特殊判断
if (j == len) continue; // 无法提供字母顺序信息
int alpha1 = words[i - 1][j] - 'a', alpha2 = words[i][j] - 'a';
if (!graph[alpha1][alpha2]) {
graph[alpha1][alpha2] = true;
inDegree[alpha2]++;
}
}
string ret;
queue<int> que;
for (int i = 0; i < 26; ++i) {
if (inDegree[i] == 0) que.push(i);
}
while (!que.empty()) {
int node = que.front();
que.pop();
ret.push_back(node + 'a');
for (int i = 0; i < 26; ++i) {
if (graph[node][i]) {
inDegree[i]--;
if (inDegree[i] == 0) que.push(i);
}
}
}
if (ret.size() != totalAlpha) return {};
return ret;
}
4. 相似的字符串
如果交换字符串 X
中的两个不同位置的字母,使得它和字符串 Y
相等,那么称 X
和 Y
两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。
例如,"tars"
和 "rats"
是相似的 (交换 0
与 2
的位置); "rats"
和 "arts"
也是相似的,但是 "star"
不与 "tars"
,"rats"
,或 "arts"
相似。
总之,它们通过相似性形成了两个关联组:{"tars", "rats", "arts"}
和 {"star"}
。注意,"tars"
和 "arts"
是在同一组中,即使它们并不相似。形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。
给定一个字符串列表 strs
。列表中的每个字符串都是 strs
中其它所有字符串的一个 字母异位词 。请问 strs
中有多少个相似字符串组?
字母异位词(anagram),一种把某个字符串的字母的位置(顺序)加以改换所形成的新词。
Example 1
输入:strs = ["tars","rats","arts","star"]
输出:2
Example 2
输入:strs = ["omv","ovm"]
输出:1
代码
class Solution {
private:
vector<int> parent;
int find(int x) {
while (x != parent[x]) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
bool check(const string &a, const string &b) {
int num = 0;
for (int i = 0; i < a.size(); ++i) {
if (a[i] != b[i]) {
if (++num > 2) return false;
}
}
return true;
}
public:
int numSimilarGroups(vector<string> &strs) {
int n = strs.size();
parent.resize(n);
for (int i = 0; i < n; ++i) parent[i] = i;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
int pi = find(i), pj = find(j);
if (pi == pj) continue;
if (check(strs[i], strs[j])) parent[pi] = pj;
}
}
int ret = 0;
for (int i = 0; i < n; ++i) {
if (parent[i] == i) ret++;
}
return ret;
}
};
5. 多余的边
树可以看成是一个连通且 无环 的 无向 图。
给定往一棵 n
个节点 (节点值 1~n
) 的树中添加一条边后的图。添加的边的两个顶点包含在 1
到 n
中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n
的二维数组 edges
,edges[i] = [ai, bi]
表示图中在 ai
和 bi
之间存在一条边。
请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n
个节点的树。如果有多个答案,则返回数组 edges
中最后出现的边。
Example 1
输入: edges = [[1,2],[1,3],[2,3]]
输出: [2,3]
Example 2
输入: edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]
输出: [1,4]
代码
class Solution {
private:
vector<int> parent;
int find(int x) {
while (x != parent[x]) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
public:
vector<int> findRedundantConnection(vector<vector<int>> &edges) {
int n = edges.size();
parent.resize(n + 1);
for (int i = 1; i <= n; ++i) parent[i] = i;
for (auto &e:edges) {
int p0 = find(e[0]), p1 = find(e[1]);
if (p0 != p1) {
parent[p0] = p1;
} else {
return e;
}
}
return {};
}
};
6. 最长连续序列
给定一个未排序的整数数组 nums
,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
Example 1
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
Example 2
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
代码
class Solution {
private:
unordered_map<int, int> mp;
int dfs(int n) {
if (mp[n] != -1) return mp[n];
mp[n] = mp.count(n + 1) ? dfs(n + 1) + 1 : 1;
return mp[n];
}
public:
int longestConsecutive(vector<int> &nums) {
for (int x:nums) mp[x] = -1;
int ans = 0;
for (int x:nums) {
ans = max(ans, dfs(x));
}
return ans;
}
};