公式

D - No-Subsequence Substring 解説 by en_translator


Let \(S _ i,T _ i\) denote the \(i\)-th character of \(S\) \((1\le i\le |S|)\) and \(T\) \(1\le i\le |T|\), respectively.

Define \(\operatorname{dp} _ {i}[j]\ (1\le i\le |S|,0\le j\le |T|)\) as follows:

  • \(\operatorname{dp} _ i[j]\coloneqq\) the number of substrings \(S _ kS _ {k+1}\cdots S _ i\ (1\le k\le i)\) of \(S\) such that:
    • \(j=0\) or the substring contains \(T _ 1T _2\cdots T _ j\) as a subsequence;
    • \(j=|T|\) or the substring does not contain \(T _ 1T _ 2\cdots T _ {j+1}\) as a subsequence.

This DP (Dynamic Programming) can be computed as follows. (It might be easier to implement as a distributing DP.)

\[\begin{aligned}\operatorname{dp} _ i[j]={}&\begin{cases}1&\quad&(j=0\wedge S _ i\ne T _ 1)\\0&&(\text{otherwise})\end{cases}\\&{}+\begin{cases}1&\quad&(j=1\wedge S _ i=T _ 1)\\0&&(\text{otherwise})\end{cases}\\&{}+\begin{cases}\operatorname{dp} _ {i-1}[j]&\quad&(j\lt|T|\wedge S _ i\ne T _ {j+1})\\0&&(\text{otherwise})\end{cases}\\&{}+\begin{cases}\operatorname{dp} _ {i-1}[j-1]&\quad&(S _ i=T _ j)\\0&&(\text{otherwise}).\end{cases}\end{aligned}\]

What we want to find is \(\displaystyle\sum _ {i=1} ^ {|S|}\sum _ {j=0} ^ {|T|-1}\operatorname{dp} _ i[j]\). This can be evaluated in \(O(|S||T|)\) time, which is fast enough. To find the answer, one may subtract \(\displaystyle\sum _ {i=1} ^ {|S|}\operatorname{dp} _ i[|T|]\) from the number of all non-empty substrings \(\dfrac{|S|(|S|+1)}2\).

The following is sample code. Updating the DP table in-place may help simplify the implementation.

#include <iostream>
#include <string>
#include <vector>
#include <ranges>
#include <algorithm>

int main() {
    using namespace std;
    string S, T;
    cin >> S >> T;

    vector<unsigned> dp(size(T) + 1);

    unsigned long ans = 0;
    for (const auto s : S) { // Inspect each character s of S in order
        ++dp.front();
        for (const auto i : views::enumerate(T) | views::filter([s](const auto& t){return s == get<1>(t);}) | views::reverse | views::keys) { // for each i with s = T[i], in descending order
            dp[i + 1] += dp[i]; // update the DP table
            dp[i] = 0;
        }
        ans += ranges::fold_left(dp | views::take(size(T)), 0UL, plus{}); // add the sum of the DP table to the answer
    }
    cout << ans << endl;
    return 0;
}

投稿日時:
最終更新: