题目描述
有一组单词,如果两个单词之间存在重叠部分(不包括包含关系,即at和attch
),则可以拼凑起来,问在这一组单词中,最长可以拼凑成的长度。
输入格式
输出格式
数据范围
n ≤ 20 n≤20 n≤20
输入样例
5
at
touch
cheat
choose
tact
a
输出样例
23
算法
本题的关键在于预处理部分,即判断两个单词之间的最小重叠部分,当然必须是首尾那种。接着就是爆搜,注意搜索顺序,每个状态下面都有n个情况,(对应递归树下层有n个分支),判断是否可以,接着就继续递归即可。至于找最大长度,每次到达一个状态,都计算一下最大长度即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 30;
int g[N][N];
string arr[N];
int st[N];
int n;
int res = 0;
string maxRes;
void dfs(string & s, int idx) { //上一个的下标是
// res = max(res, (int)s.length());
if (s.length() > res) {
res = s.length();
maxRes = s;
}
for (int i = 0; i < n; ++ i) {
if (g[idx][i] > 0 && st[i] > 0) {
int k = g[idx][i];
st[i] --;
string t = s + arr[i].substr(k);
dfs(t, i);
st[i] ++;
}
}
}
int main() {
// 数据读入
cin >> n;
for (int i = 0; i < n; ++ i) {
cin >> arr[i];
}
string s;
cin >> s;
// 每个单词只能访问两次
fill(st, st + N, 2);
// 预处理
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
string a = arr[i], b = arr[j];
int la = a.length(), lb = b.length(), cnt = min(la, lb);
for (int k = 1; k < cnt; ++ k) { // 这里是小于,非常巧妙,解决了相同时,和包含时的问题
string t1 = a.substr(la-k, k), t2 = b.substr(0, k);
if (t1 == t2) {
g[i][j] = k;
break;
}
}
}
}
for (int i = 0; i < n; ++ i) {
if (arr[i][0] == s[0]) {
st[i] --;
dfs(arr[i], i); // 上一个单词是arr[i]
st[i] ++;
}
}
cout << res << endl;
return 0;
}