动态规划 
      
    
      
      
      
        图 
      
    
   
  
    
      
       
     
  
  
    
      
    
    
      
       
     
  
题目描述 
我们有 n 座城市和 m 条双向道路 roads ,其中 roads[i] = [ai , bi ] 连接城市 ai  和城市 bi 。每个城市的名称由字符串数组 names 中给出的三个大写英文字母组成。从任意城市 x 出发,你可以到达任意城市 y ,其中 y != x (即:城市和道路形成一张无向连通图)。
给定一个字符串数组 targetPath,你需要找出图中与 targetPath 的 长度相同  且 编辑距离 最小  的路径。
你需要返回  编辑距离最小的路径中节点的顺序   。该路径应当与 targetPath 的长度相等,且路径需有效(即: ans[i] 和 ans[i + 1] 间应存在直接连通的道路)。如果有多个答案,返回任意一个。
编辑距离  的定义如下:
 
示例 1: 
输入: n = 5, roads = [[0,2],[0,3],[1,2],[1,3],[1,4],[2,4]], names = ["ATL","PEK","LAX","DXB","HND"], targetPath = ["ATL","DXB","HND","LAX"]
输出: [0,2,4,2]
解释: [0,2,4,2], [0,3,0,2] 和 [0,3,1,2] 都是正确答案。
[0,2,4,2] 等价于 ["ATL","LAX","HND","LAX"] ,与 targetPath 的编辑距离 = 1。
[0,3,0,2] 等价于 ["ATL","DXB","ATL","LAX"] ,与 targetPath 的编辑距离 = 1。
[0,3,1,2] 等价于 ["ATL","DXB","PEK","LAX"] ,与 targetPath 的编辑距离 = 1。
 
示例 2: 
输入: n = 4, roads = [[1,0],[2,0],[3,0],[2,1],[3,1],[3,2]], names = ["ATL","PEK","LAX","DXB"], targetPath = ["ABC","DEF","GHI","JKL","MNO","PQR","STU","VWX"]
输出: [0,1,0,1,0,1,0,1]
解释: 任意路径与 targetPath 的编辑距离都等于 8。
 
示例 3: 
输入: n = 6, roads = [[0,1],[1,2],[2,3],[3,4],[4,5]], names = ["ATL","PEK","LAX","ATL","DXB","HND"], targetPath = ["ATL","DXB","HND","DXB","ATL","LAX","PEK"]
输出: [3,4,5,4,3,2,1]
解释: [3,4,5,4,3,2,1] 是唯一与 targetPath 的编辑距离 = 0 的路径。
该路径等价于 ["ATL","DXB","HND","DXB","ATL","LAX","PEK"]
 
 
提示: 
    2 <= n <= 100 
    m == roads.length 
    n - 1 <= m <= (n * (n - 1) / 2) 
    0 <= ai , bi  <= n - 1 
    ai  != bi   
    给定的图保证是连通 的,任意两个节点至多有一个 直接连通的道路。 
    names.length == n 
    names[i].length == 3 
    names[i] 包含大写英文字母。 
    可能有两个名称相同 的城市。 
    1 <= targetPath.length <= 100 
    targetPath[i].length == 3 
    targetPath[i] 由大写英文字母组成。 
 
 
进阶: 如果路径中每个节点只可访问一次,你该如何修改你的答案?
解法 
方法一:动态规划 
我们先根据给定的道路构建一个邻接表 \(g\) ,其中 \(g[i]\)  表示与城市 \(i\)  直接相连的城市列表。
然后我们定义 \(f[i][j]\)  表示 \(targetPath\)  的第 \(i\)  个城市与 \(names\)  的第 \(j\)  个城市匹配时,前 \(i\)  个城市的最小编辑距离。
那么我们可以得到状态转移方程:
\[
f[i][j] = \min_{k \in g[j]} f[i - 1][k] + (targetPath[i] \neq names[j])
\]
在状态转移的过程中,我们记录下每个状态的前驱城市,最后根据前驱城市数组 \(pre\)  从后往前还原出最优路径。
时间复杂度 \(O(m \times n^2)\) ,空间复杂度 \(O(m \times n)\) 。其中 \(m\)  和 \(n\)  分别是 \(targetPath\)  和 \(names\)  的长度。
Python3 Java C++ Go TypeScript 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 class   Solution : 
    def   mostSimilar ( 
        self ,  n :  int ,  roads :  List [ List [ int ]],  names :  List [ str ],  targetPath :  List [ str ] 
    )  ->  List [ int ]: 
        g  =  [[]  for  _  in  range ( n )] 
        for  a ,  b  in  roads : 
            g [ a ] . append ( b ) 
            g [ b ] . append ( a ) 
        m  =  len ( targetPath ) 
        f  =  [[ inf ]  *  n  for  _  in  range ( m )] 
        pre  =  [[ - 1 ]  *  n  for  _  in  range ( m )] 
        for  j ,  s  in  enumerate ( names ): 
            f [ 0 ][ j ]  =  targetPath [ 0 ]  !=  s 
        for  i  in  range ( 1 ,  m ): 
            for  j  in  range ( n ): 
                for  k  in  g [ j ]: 
                    if  ( t  :=  f [ i  -  1 ][ k ]  +  ( targetPath [ i ]  !=  names [ j ]))  <  f [ i ][ j ]: 
                        f [ i ][ j ]  =  t 
                        pre [ i ][ j ]  =  k 
        k  =  0 
        mi  =  inf 
        for  j  in  range ( n ): 
            if  f [ - 1 ][ j ]  <  mi : 
                mi  =  f [ - 1 ][ j ] 
                k  =  j 
        ans  =  [ 0 ]  *  m 
        for  i  in  range ( m  -  1 ,  - 1 ,  - 1 ): 
            ans [ i ]  =  k 
            k  =  pre [ i ][ k ] 
        return  ans 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 class  Solution   { 
     public   List < Integer >   mostSimilar ( int   n ,   int [][]   roads ,   String []   names ,   String []   targetPath )   { 
         List < Integer >[]   g   =   new   List [ n ] ; 
         Arrays . setAll ( g ,   i   ->   new   ArrayList <> ()); 
         for   ( int []   r   :   roads )   { 
             int   a   =   r [ 0 ] ,   b   =   r [ 1 ] ; 
             g [ a ] . add ( b ); 
             g [ b ] . add ( a ); 
         } 
         int   m   =   targetPath . length ; 
         final   int   inf   =   1   <<   30 ; 
         int [][]   f   =   new   int [ m ][ n ] ; 
         int [][]   pre   =   new   int [ m ][ n ] ; 
         for   ( int   i   =   0 ;   i   <   m ;   i ++ )   { 
             Arrays . fill ( f [ i ] ,   inf ); 
             Arrays . fill ( pre [ i ] ,   - 1 ); 
         } 
         for   ( int   j   =   0 ;   j   <   n ;   ++ j )   { 
             f [ 0 ][ j ]   =   targetPath [ 0 ] . equals ( names [ j ] )   ?   0   :   1 ; 
         } 
         for   ( int   i   =   1 ;   i   <   m ;   ++ i )   { 
             for   ( int   j   =   0 ;   j   <   n ;   ++ j )   { 
                 for   ( int   k   :   g [ j ] )   { 
                     int   t   =   f [ i   -   1 ][ k ]   +   ( targetPath [ i ] . equals ( names [ j ] )   ?   0   :   1 ); 
                     if   ( t   <   f [ i ][ j ] )   { 
                         f [ i ][ j ]   =   t ; 
                         pre [ i ][ j ]   =   k ; 
                     } 
                 } 
             } 
         } 
         int   mi   =   inf ,   k   =   0 ; 
         for   ( int   j   =   0 ;   j   <   n ;   ++ j )   { 
             if   ( f [ m   -   1 ][ j ]   <   mi )   { 
                 mi   =   f [ m   -   1 ][ j ] ; 
                 k   =   j ; 
             } 
         } 
         List < Integer >   ans   =   new   ArrayList <> (); 
         for   ( int   i   =   m   -   1 ;   i   >=   0 ;   -- i )   { 
             ans . add ( k ); 
             k   =   pre [ i ][ k ] ; 
         } 
         Collections . reverse ( ans ); 
         return   ans ; 
     } 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 class   Solution   { 
public : 
     vector < int >   mostSimilar ( int   n ,   vector < vector < int >>&   roads ,   vector < string >&   names ,   vector < string >&   targetPath )   { 
         vector < int >   g [ n ]; 
         for   ( auto &   r   :   roads )   { 
             int   a   =   r [ 0 ],   b   =   r [ 1 ]; 
             g [ a ]. push_back ( b ); 
             g [ b ]. push_back ( a ); 
         } 
         int   m   =   targetPath . size (); 
         int   f [ m ][ n ]; 
         int   pre [ m ][ n ]; 
         memset ( f ,   0x3f ,   sizeof ( f )); 
         memset ( pre ,   -1 ,   sizeof ( pre )); 
         for   ( int   j   =   0 ;   j   <   n ;   ++ j )   { 
             f [ 0 ][ j ]   =   targetPath [ 0 ]   !=   names [ j ]; 
         } 
         for   ( int   i   =   1 ;   i   <   m ;   ++ i )   { 
             for   ( int   j   =   0 ;   j   <   n ;   ++ j )   { 
                 for   ( int   k   :   g [ j ])   { 
                     int   t   =   f [ i   -   1 ][ k ]   +   ( targetPath [ i ]   !=   names [ j ]); 
                     if   ( t   <   f [ i ][ j ])   { 
                         f [ i ][ j ]   =   t ; 
                         pre [ i ][ j ]   =   k ; 
                     } 
                 } 
             } 
         } 
         int   k   =   0 ; 
         int   mi   =   1   <<   30 ; 
         for   ( int   j   =   0 ;   j   <   n ;   ++ j )   { 
             if   ( f [ m   -   1 ][ j ]   <   mi )   { 
                 mi   =   f [ m   -   1 ][ j ]; 
                 k   =   j ; 
             } 
         } 
         vector < int >   ans ( m ); 
         for   ( int   i   =   m   -   1 ;   ~ i ;   -- i )   { 
             ans [ i ]   =   k ; 
             k   =   pre [ i ][ k ]; 
         } 
         return   ans ; 
     } 
}; 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 func   mostSimilar ( n   int ,   roads   [][] int ,   names   [] string ,   targetPath   [] string )   [] int   { 
     g   :=   make ([][] int ,   n ) 
     for   _ ,   r   :=   range   roads   { 
         a ,   b   :=   r [ 0 ],   r [ 1 ] 
         g [ a ]   =   append ( g [ a ],   b ) 
         g [ b ]   =   append ( g [ b ],   a ) 
     } 
     m   :=   len ( targetPath ) 
     const   inf   =   1   <<   30 
     f   :=   make ([][] int ,   m ) 
     pre   :=   make ([][] int ,   m ) 
     for   i   :=   range   f   { 
         f [ i ]   =   make ([] int ,   n ) 
         pre [ i ]   =   make ([] int ,   n ) 
         for   j   :=   range   f [ i ]   { 
             f [ i ][ j ]   =   inf 
             pre [ i ][ j ]   =   - 1 
         } 
     } 
     for   j ,   s   :=   range   names   { 
         if   targetPath [ 0 ]   !=   s   { 
             f [ 0 ][ j ]   =   1 
         }   else   { 
             f [ 0 ][ j ]   =   0 
         } 
     } 
     for   i   :=   1 ;   i   <   m ;   i ++   { 
         for   j   :=   0 ;   j   <   n ;   j ++   { 
             for   _ ,   k   :=   range   g [ j ]   { 
                 t   :=   f [ i - 1 ][ k ] 
                 if   targetPath [ i ]   !=   names [ j ]   { 
                     t ++ 
                 } 
                 if   t   <   f [ i ][ j ]   { 
                     f [ i ][ j ]   =   t 
                     pre [ i ][ j ]   =   k 
                 } 
             } 
         } 
     } 
     mi ,   k   :=   inf ,   0 
     for   j   :=   0 ;   j   <   n ;   j ++   { 
         if   f [ m - 1 ][ j ]   <   mi   { 
             mi   =   f [ m - 1 ][ j ] 
             k   =   j 
         } 
     } 
     ans   :=   make ([] int ,   m ) 
     for   i   :=   m   -   1 ;   i   >=   0 ;   i --   { 
         ans [ i ]   =   k 
         k   =   pre [ i ][ k ] 
     } 
     return   ans 
} 
 
 
 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 function   mostSimilar ( 
     n :   number , 
     roads :   number [][], 
     names :   string [], 
     targetPath :   string [], 
) :   number []   { 
     const   g :   number [][]   =   Array . from ({   length :   n   },   ()   =>   []); 
     for   ( const   [ a ,   b ]   of   roads )   { 
         g [ a ]. push ( b ); 
         g [ b ]. push ( a ); 
     } 
     const   m   =   targetPath . length ; 
     const   f   =   Array . from ({   length :   m   },   ()   =>   Array . from ({   length :   n   },   ()   =>   Infinity )); 
     const   pre :   number [][]   =   Array . from ({   length :   m   },   ()   =>   Array . from ({   length :   n   },   ()   =>   - 1 )); 
     for   ( let   j   =   0 ;   j   <   n ;   ++ j )   { 
         f [ 0 ][ j ]   =   names [ j ]   ===   targetPath [ 0 ]   ?   0   :   1 ; 
     } 
     for   ( let   i   =   1 ;   i   <   m ;   ++ i )   { 
         for   ( let   j   =   0 ;   j   <   n ;   ++ j )   { 
             for   ( const   k   of   g [ j ])   { 
                 const   t   =   f [ i   -   1 ][ k ]   +   ( names [ j ]   ===   targetPath [ i ]   ?   0   :   1 ); 
                 if   ( t   <   f [ i ][ j ])   { 
                     f [ i ][ j ]   =   t ; 
                     pre [ i ][ j ]   =   k ; 
                 } 
             } 
         } 
     } 
     let   k   =   0 ; 
     let   mi   =   Infinity ; 
     for   ( let   j   =   0 ;   j   <   n ;   ++ j )   { 
         if   ( f [ m   -   1 ][ j ]   <   mi )   { 
             mi   =   f [ m   -   1 ][ j ]; 
             k   =   j ; 
         } 
     } 
     const   ans :   number []   =   Array ( m ). fill ( 0 ); 
     for   ( let   i   =   m   -   1 ;   ~ i ;   -- i )   { 
         ans [ i ]   =   k ; 
         k   =   pre [ i ][ k ]; 
     } 
     return   ans ; 
} 
 
 
 
 
  
  
  
    
    
    
    
      
  
    
      
  
     
   
  GitHub