zoukankan      html  css  js  c++  java
  • 1202. Smallest String With Swaps

    问题:

    给定字符串s,和可进行交换的index对数组。

    对字符串s的各个位置index,可根据交换数组所示的两两交换(次数不限),求进行交换后,可得最小的字符串。

    Example 1:
    Input: s = "dcab", pairs = [[0,3],[1,2]]
    Output: "bacd"
    Explaination: 
    Swap s[0] and s[3], s = "bcad"
    Swap s[1] and s[2], s = "bacd"
    
    Example 2:
    Input: s = "dcab", pairs = [[0,3],[1,2],[0,2]]
    Output: "abcd"
    Explaination: 
    Swap s[0] and s[3], s = "bcad"
    Swap s[0] and s[2], s = "acbd"
    Swap s[1] and s[2], s = "abcd"
    
    Example 3:
    Input: s = "cba", pairs = [[0,1],[1,2]]
    Output: "abc"
    Explaination: 
    Swap s[0] and s[1], s = "bca"
    Swap s[1] and s[2], s = "bac"
    Swap s[0] and s[1], s = "abc"
     
    Constraints:
    1 <= s.length <= 10^5
    0 <= pairs.length <= 10^5
    0 <= pairs[i][0], pairs[i][1] < s.length
    s only contains lower case English letters.
    

      

    解法:

    本问题为:连通图问题

    对两组可交换的index对,出现重复index的话,那么这两个组之间的3个index都可以任意交换。

    那么,题意可转换为:

    在给出的可交换对数组中,可形成多个可任意交换的index序列,

    这几个序列之间没有交集,但是序列内部可进行随意排序,

    求出每个序列的最小排序,并将这些序列合并。

    本问题的关键:连通图的构造

    方法一:

    dfs:深度优先排序:

    对图中的每一个未访问过的节点a(一个连通图),轮询:

        对每个节点a 的邻接点 adjList,依次轮询标记,标记为一个组。graphid

        访问过的节点,要标记 visited

    最后,graphid相同的即为一个连通图。

    这里,对每一个连通图,要做固定操作的话,可在标记graphid的同时,直接进行操作。

    (对于本题,则是记录:当前连通图的节点 idx[] )

    (在每一个连通图遍历完毕的时候,对重新取得节点对应的字符,组成新的字符串tmp,对tmp排序,再将新的排序赋值回原字符串s中)

    代码参考:

     1 class Solution {
     2 public:
     3     vector<bool> visited;
     4     vector<vector<int>> adjList;
     5     string tmp;
     6     vector<int> idx;
     7     
     8     void dfs(int i, string& s){
     9         visited[i]=true;//marked->0 设置访问
    10         tmp.push_back(s[i]);
    11         idx.push_back(i);//同一层连通图
    12         for(int p:adjList[i]){
    13             if(!visited[p]){//marked==0 未访问过
    14                 dfs(p, s);
    15             }
    16         }
    17     }
    18     string makepinfo(string& s){
    19         string res(s.length(),0);
    20         for(int i=0; i<s.length(); i++){
    21             tmp="";
    22             idx.clear();
    23             if(!visited[i]){//未访问过
    24                 dfs(i, s);
    25                 sort(tmp.begin(), tmp.end());
    26                 sort(idx.begin(), idx.end());
    27                 for(int t=0; t<tmp.size(); t++){
    28                     res[idx[t]]=tmp[t];
    29                 }
    30             }
    31         }
    32         return res;
    33     }
    34     
    35     string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) {
    36         visited.resize(s.length(),false);
    37         adjList.resize(s.length());
    38         for(auto p:pairs){
    39             adjList[p[0]].push_back(p[1]);
    40             adjList[p[1]].push_back(p[0]);
    41         }
    42         return makepinfo(s);
    43     }
    44 };

    方法二:

    直接使用

    数组vector un表示是否为一个连通图:

    un[i]:元素i的邻接元素。

    1         UN(int n){//初始化连通组,自己的下一个为自己,每个元素孤立存在
    2             un.resize(n);
    3             for(int u=0; u<n; u++){
    4                 un[u]=u;
    5             }
    6         }

    un.unite(i, j):链接两个元素。

    (使用find,寻找两个节点的终点)

    (如果终点相同,说明两元素已经在同一个连通图中)

    (否则,将 i 的终点 un[fi] 指向 j )

    1         void unite(int i, int j){//合并连通组 i和j
    2             int fi=find(i);
    3             int fj=find(j);
    4             if(fi!=fj){//两个连通图的终点不一样,则合并连通图i和j
    5                 un[fi]=j;
    6             }//否则,i和j是连通图
    7         }

    un.find(i):找到元素i所在连通图的终点节点。

    1         int find(int i){
    2             int x=un[i];
    3             if(un[x]!=x){
    4                 un[i]=find(x);
    5             }
    6             return un[i];
    7         }

    那么对于本题,首先构造un

    对每一对pair,进行unite。

    构造完毕后,构造连通图组group

    key:连通图终点元素,value:连通图的各个节点。

    1         unordered_map<int, vector<int>> group;
    2         //使用连通图UN,做成连通图组,key:UN每个连通图的终点,value:连通图的各个节点
    3         for(int i=0; i<N; i++){
    4             group[un.find(i)].push_back(i);
    5         }

    然后遍历每个连通图,进行string tmp排序

    并合并各个tmp的动作。

     1         //使用group排序每个连通图的string
     2         for(auto g:group){
     3             string tmp("");
     4             for(int idx:g.second){
     5                 tmp.push_back(s[idx]);
     6             }
     7             sort(tmp.begin(), tmp.end());
     8             int i=0;
     9             for(int idx:g.second){
    10                 s[idx]=tmp[i];
    11                 i++;
    12             }
    13         }

    总代码参考:

     1 class Solution {
     2 public:
     3     class UN {
     4         public:
     5         vector<int> un;
     6         UN(int n){//初始化连通组,自己的下一个为自己,每个元素孤立存在
     7             un.resize(n);
     8             for(int u=0; u<n; u++){
     9                 un[u]=u;
    10             }
    11         }
    12         int find(int i){
    13             int x=un[i];
    14             if(un[x]!=x){
    15                 un[i]=find(x);
    16             }
    17             return un[i];
    18         }
    19         void unite(int i, int j){//合并连通组 i和j
    20             int fi=find(i);
    21             int fj=find(j);
    22             if(fi!=fj){//两个连通图的终点不一样,则合并连通图i和j
    23                 un[fi]=j;
    24             }//否则,i和j是连通图
    25         }
    26     };
    27     
    28     string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) {
    29         int N=s.length();
    30         UN un(N);
    31         for(auto p:pairs){//使用pairs,做成连通图UN
    32             un.unite(p[0], p[1]);
    33         }
    34         unordered_map<int, vector<int>> group;
    35         //使用连通图UN,做成连通图组,key:UN每个连通图的终点,value:连通图的各个节点
    36         for(int i=0; i<N; i++){
    37             group[un.find(i)].push_back(i);
    38         }
    39         //使用group排序每个连通图的string
    40         for(auto g:group){
    41             string tmp("");
    42             for(int idx:g.second){
    43                 tmp.push_back(s[idx]);
    44             }
    45             sort(tmp.begin(), tmp.end());
    46             int i=0;
    47             for(int idx:g.second){
    48                 s[idx]=tmp[i];
    49                 i++;
    50             }
    51         }
    52         return s;
    53     }
    54 };
  • 相关阅读:
    SDK Hello world(直接使用SDK封装)
    一个类有多个基类的内存布局
    写个测试程序看看磁盘映像文件中哪个扇区可以使用?
    在服务中以当前用户身份启动一个程序
    在类有成员变量的场景下, 按照虚表原理, 模拟虚函数实现
    pipe----管道
    Qt序列化格式分析(qint,QString)(非常简单好用)
    模拟QQ系统设置面板实现功能
    Qt持久性对象进行序列化(同时比较了MFC与Java的方法)
    使用srvany.exe将任何程序作为Windows服务运行
  • 原文地址:https://www.cnblogs.com/habibah-chang/p/13168838.html
Copyright © 2011-2022 走看看