2022-5-23[图] 省份数量, 重建序列, 外星文字典, 相似的字符串, 多余的边, 最长连续序列

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 1n104

重建 是指在序列集 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 相等,那么称 XY 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。

例如,"tars""rats" 是相似的 (交换 02 的位置); "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) 的树中添加一条边后的图。添加的边的两个顶点包含在 1n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edgesedges[i] = [ai, bi] 表示图中在 aibi 之间存在一条边。

请找出一条可以删去的边,删除后可使得剩余部分是一个有着 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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值