字符串的题目,套路不是动态规划就是转化为图。
本题把每个string作为节点,s1 -> s2 边的权值为 把s2接到s1后还需要添加的字符数量。本题就转化为类似 TSP 的问题,TSP 可以用动归来做。
复习一下TSP:
本题思路和 TSP 思路一致。dp 初始条件设置为 C({i},i) = A[i].size() 即可。
由于要枚举集合,方便起见,可以用二进制来表示,如 (101)_2 就表示 {2,0} 的集合。由于集合必须由size从小到大枚举,0 ~ (1<<n)-1 也刚好从小到大。[位运算优先级非常低,注意加括号!]
由于最后要返回字符串,还需要 bp 来记录 argmin 的值(也就是使得 C(S,j)最小的i的值),这样最后就可以从 min dp[(1<<n)-1][j] 一步步反推。
class Solution { public: string shortestSuperstring(vector<string>& A) { int n=A.size(); vector<vector<int>> g(n,vector<int>(n)); for (int i=0;i<n;++i){ for (int j=i+1;j<n;++j){ g[i][j] = dist(A[i],A[j]); g[j][i] = dist(A[j],A[i]); } } for (int i=0;i<n;++i){ for (int j=0;j<n;++j){ cout << g[i][j] << ' '; } cout<<endl; } // dp(S,j) = min_i (i\in S: i!=j) dp(S-{j},i) + d_{ij} vector<vector<int>> dp(1<<n,vector<int>(n,INT_MAX/2)); // backpointer, bp(S,j) = argmin_i (i\in S: i!=j) dp(S-{j},i) + d_{ij} vector<vector<int>> bp(1<<n,vector<int>(n,-1)); for (int j=0;j<n;++j) dp[1<<j][j]=A[j].size(); for (int s=1;s<(1<<n);++s){ for (int j=0;j<n;++j){ if (!(s & (1<<j))) continue; // j must be in S int prev=s-(1<<j); for (int i=0;i<n;++i){ if (!(s & (1<<i)) || i==j) continue; if (dp[prev][i]+g[i][j] < dp[s][j]){ dp[s][j] = dp[prev][i]+g[i][j]; bp[s][j] = i; } } // cout << s << ' ' << j << ' '<< dp[s][j] <<endl; } } int min=INT_MAX, argmin; for (int j=0;j<n;++j){ if (dp[(1<<n)-1][j] < min){ min = dp[(1<<n)-1][j]; argmin = j; } } int s=(1<<n)-1; int cur=argmin; string ans=""; while (s){ int prev=bp[s][cur]; if (prev<0) ans=A[cur]+ans; else ans=A[cur].substr(A[cur].size()-g[prev][cur])+ans; s &= ~(1<<cur); cur = prev; } return ans; } // if put b after a, at least how many letters will be appended int dist(const string &a, const string &b){ int res; for (int overlap=0;overlap<=min(a.size(),b.size());++overlap){ if (a.substr(a.size()-overlap) == b.substr(0,overlap)) res = b.size()-overlap; } return res; } };
时间复杂度同 TSP
转载于:https://www.cnblogs.com/hankunyan/p/11097412.html