東京大学プログラミングコンテスト2012: G - k番目の文字列
反省
部分文字列でなくて部分列だと誤読。 「ただし,アリスは夢を見ていて,このような並べ方は本当は存在しないかもしれない. その場合には,0を答えよ.」とあるが、これを$-1$と誤読。 そもそもまったく分からない。部分点をちゃんと見てれば思い付けていたのだろうか。
solution
DP。$s$の位置$\mathrm{offset} \le n$を固定し前から$i \le n$文字目まで固定して$s_0$より小さい数を$j \le$個使って$s$より小さい部分文字列の数が$l \le k \le n^2$個確定しているような状態の数を$\mathrm{dp}(\mathrm{offset}, i, j, l)$とおく。$O(n^5)$。 遷移の肝は、位置$i$に文字$c \lt s_0$があればこの$c$を先頭とする$n - i$個の部分文字列は$s$より小さいことが確定し、$c \gt s_0$であればこの数は$0$であること。
implementation
#include <bits/stdc++.h>
#define REP(i, n) for (int i = 0; (i) < int(n); ++ (i))
#define REP3(i, m, n) for (int i = (m); (i) < int(n); ++ (i))
#define REP_R(i, n) for (int i = int(n) - 1; (i) >= 0; -- (i))
#define REP3R(i, m, n) for (int i = int(n) - 1; (i) >= int(m); -- (i))
using ll = long long;
using namespace std;
template <typename X, typename T> auto vectors(X x, T a) { return vector<T>(x, a); }
template <typename X, typename Y, typename Z, typename... Zs> auto vectors(X x, Y y, Z z, Zs... zs) { auto cont = vectors(y, z, zs...); return vector<decltype(cont)>(x, cont); }
template <int MOD>
int fact(int n) {
static vector<int> memo(1, 1);
while (n >= memo.size()) {
memo.push_back(memo.back() *(ll) memo.size() % MOD);
}
return memo[n];
}
constexpr ll mod = 1000000007;
int main() {
// input
int n, k; cin >> n >> k;
string s; cin >> s;
// solve
int lower = 0;
REP3 (c, 'a', s[0]) {
lower += (s.find(c) == string::npos);
}
ll result = 0;
REP (offset, n - s.length() + 1) {
auto cur = vectors(lower + 1, k + 1, -1);
auto prv = vectors(lower + 1, k + 1, -1);
cur[0][0] = 1;
REP (i, n) {
// REP (j, lower + 1) REP (l, k + 1) if (cur[j][l] != -1) fprintf(stderr, "dp[%d][%d][%d][%d] = %d\n", offset, i, j, l, cur[j][l]);
if (offset <= i and i < offset + s.length()) {
int delta = -1;
if (s[i - offset] == s[0]) {
delta = s.length();
} else if (s[i - offset] < s[0]) {
delta = n - i;
}
if (delta != -1) {
delta = min(delta, k + 1);
REP (j, lower + 1) {
REP3R (l, delta, k + 1) {
cur[j][l] = cur[j][l - delta];
}
REP_R (l, delta) {
cur[j][l] = -1;
}
}
}
} else {
cur.swap(prv);
REP (j, lower + 1) {
REP (l, k + 1) {
bool exists = false;
ll acc = 0;
if (prv[j][l] != -1) {
exists = true;
acc += prv[j][l];
}
int pj = j - 1;
int pl = l - (n - i);
if (pj >= 0 and pl >= 0 and prv[pj][pl] != -1) {
exists = true;
acc += prv[pj][pl];
}
cur[j][l] = exists ? acc % mod : -1;
}
}
}
}
if (cur[lower][k] != -1) {
result += cur[lower][k];
result %= mod;
}
// REP (j, lower + 1) REP (l, k + 1) if (cur[j][l] != -1) fprintf(stderr, "dp[%d][%d][%d][%d] = %d\n", offset, n, j, l, cur[j][l]);
}
result = result *(ll) fact<mod>(lower) % mod;
result = result *(ll) fact<mod>(n - lower - s.length()) % mod;
// output
printf("%lld\n", result);
return 0;
}