1. 程式人生 > >【NOIP2015提高組】子串

【NOIP2015提高組】子串

false syn style 上一個 blog 表示 tps clas turn

https://daniu.luogu.org/problem/show?pid=2679

看到方案數問題直覺就能想到DP,考慮用f(i,j,k)表示A[1...i]取k個子串組成B[1...j]的方案數,發現很難轉移,因為不知道之前的方案哪些是還能拼接到結尾的,產生了前效性。

考慮加一維,即

A[1...i]取k個子串組成B[1...j],且末尾子串還可以繼續拼接的方案數為:f(i,j,k,0)={
拼接上一個子串f(i-1,j-1,k,0)+另開新串f(i-1,j-1,k-1,1) (A[i]==B[j]),
0 (A[i]!=B[j])
}


A[1...i]取k個子串組成B[1...j],且末尾子串已經封閉或末尾根本不是子串的方案數為:f(i,j,k,1)=sum{
拼接上一個子串然後封閉

f(i-1,j-1,k,0) (A[i]==B[j]),
開一個字符的串f(i-1,j-1,k-1,1) (A[i]==B[j]),
不用這個字符f(i-1,j,k,1)
}
其實就是f(i,j,k,1)=用這個字符然後封閉f(i,j,k,0)+不用這個字符f(i-1,j,k,1)

特別的,f(i,0,0,1)=1

#include <iostream>
#include <string>
using namespace std;
typedef long long llint;
llint n, m, kk;
string a, b;
const
llint c = 1e9 + 7; llint dp[2][205][205][2]; int main() { ios::sync_with_stdio(false); cin >> n >> m >> kk >> a >> b; // f(i,j,k,0) = a[i]==b[j] ? f(i-1,j-1,k,0)+f(i-1,j-1,k-1,1) : 0 // f(i,j,k,1) = f(i,j,k,0) + f(i-1,j,k,1) dp[0][0][0][1] = dp[1][0][0][1] = 1; // f(i,0,0,1)=1
for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { for (int k = 1; k <= kk; k++) { dp[i & 1][j][k][0] = (a[i - 1] == b[j - 1]) ? dp[(i - 1) & 1][j - 1][k][0] + dp[(i - 1) & 1][j - 1][k - 1][1] : 0; dp[i & 1][j][k][1] = dp[i & 1][j][k][0] + dp[(i - 1) & 1][j][k][1]; while (dp[i & 1][j][k][0] >= c) dp[i & 1][j][k][0] -= c; while (dp[i & 1][j][k][1] >= c) dp[i & 1][j][k][1] -= c; } } } cout << dp[n & 1][m][kk][1] << endl; return 0; }

【NOIP2015提高組】子串