cf346 B. Lucky Common Subsequence
题意:
输出串 a 和串 b 的,不含串 c 作为子串的最长公共子列。
串长 100
思路:
\(f(i,j,k)\) 表示从 \(a[1\sim i]\) 和 \(b[1\sim j]\) 中选,末端匹配到串 c 的第 \(k\) 个位置(即含 \(c[1\sim k]\) 作为后缀,\(k\) 最大)的最长公共子列。
dp 做 LCS 的过程中,\(a_i\neq b_j\) 时正常更新,即 \(f(i,j)=\max (f(i-1,j),f(i,j-1))\)
\(a_i\neq b_j\) 时,考虑往 \(f(i-1,j-1,k)\) 的末尾添个 \(a_i\) 会变成啥状态。这是一个 kmp 过程,不断 k=ne[k] 往前找到一个能扩展的 c 前缀 \(t-1\),那就可以用 \(f(i-1,j-1,k)\) 更新 \(f(i,j,t)\)
要输出子列,懒得写回溯,全用 string 了
某人为了让string的下标从1开始而疯狂在串首加东西的的样子真狼狈啊。输出答案时记得去掉
const signed N = 103;
int na, nb, nc, ne[N];
string a, b, c, f[N][N][N], ans;
void upd(string &a, string b) {
if(a.size() < b.size()) a = b;
}
signed main() {
iofast;
cin >> a >> b >> c;
na = a.size(), nb = b.size(), nc = c.size();
fill_n(f[0][0], N*N*N, "0"); //奇怪的初始化
a = "0" + a; b = "0" + b; c = "0" + c;
//getNext
for(int i = 2, j = 0; i <= nc; i++) {
while(j && c[i] != c[j+1]) j = ne[j];
if(c[i] == c[j+1]) j++;
ne[i] = j;
}
for(int i = 1; i <= na; i++)
for(int j = 1; j <= nb; j++)
for(int k = 0; k < nc; k++) {
if(a[i] == b[j]) {
int t = k;
while(t && a[i] != c[t+1]) t = ne[t];
if(a[i] == c[t+1]) t++;
upd(f[i][j][t], f[i-1][j-1][k] + a[i]);
}
upd(f[i][j][k], f[i][j-1][k]);
upd(f[i][j][k], f[i-1][j][k]);
}
for(int i = 0; i < nc; i++) upd(ans, f[na][nb][i]);
cout << (ans == "0" ? "0" : ans.substr(1));
}

浙公网安备 33010602011771号