很好的区间DP题目。
Description
给出两个串A,B。每次可以往A中插入一个字符。
将每次操作后的A串记为一个序列。
求从A串变为B串的不同序列数量。
Difficulty
★★★★
Main Algorithm
DP
Complexity
Solution
不妨先考虑一个简单的情况,即A为空串。
即每次插入一个字符,得到B串,这样的序列数量的统计。
考虑如何去重。什么情况下会有重复串呢?只有在B串出现了连续相同字符的时候,这时候插入这些字符的先后顺序是没有影响的。
为了去掉这一点的影响,不妨规定往连续相同字符中插入时只能插入到这一段的开头。
设
表示得到
这一段的方案数。如何在转移时将上述规定表述出来?
不妨设第一个插入的字符是
,那么
。
故转移就可得了:
.
接下来考虑怎么处理A不为空。试图将B拆成很多段,有的段与A中的一部分匹配,别的我们就可以视作是从空->有的利用上述算法得到。
设
表示A串匹配到了i,B串匹配到了j的方案数。
枚举
匹配到了
,需要满足
.
.
这样,问题就成功解决了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
const int mod = 1000000007, N = 205;
class StringSequences {
public:
int s1[N], s2[N], n, m;
LL f[N][N], g[N][N], ans, C[N][N];
int countSequences(string A, string B) {
n = A.size(), m = B.size();
cout << n << endl;
Rep(i, 1, n) s1[i] = A[i - 1];
Rep(i, 1, m) s2[i] = B[i - 1];
Rep(i, 1, m + 1) f[i][i] = 1;
C[0][0] = 1;
Rep(i, 1, m) {
C[i][0] = 1;
Rep(j, 1, i) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
}
Rep(l, 1, m) {
Rep(i, 1, m - l + 1) {
int j = i + l;
Rep(k, i, j - 1) if (s2[k] != s2[j]) (f[i][j] += (f[i][k] * f[k + 1][j] % mod) * C[j - i - 1][k - i]) %= mod;
}
}
g[0][0] = 1;
Rep(i, 1, n) {
Rep(j, 1, m) if (s1[i] == s2[j]) {
Rep(k, 0, j - 1) {
(g[i][j] += (g[i - 1][k] * f[k + 1][j] % mod) * C[m - n - k + i - 1][j - k - 1]) %= mod;
}
}
}
Rep(j, 1, m) (ans += g[n][j] * f[j + 1][m + 1]) %= mod;
return (int)ans;
}
};