質問の意味:
たった2つの文字列の小文字含まれている A と Bを
今から文字列 Aを 削除し 、k番目の重複しない非空の文字列を、これらの k個の そのに従い、文字列内のサブストリング Aの シーケンスを順次接続して表示され、新しい文字列を取得します。
私は、この新しい文字列と文字列を作ることができますどのように多くの種類のプログラムのだろ Bを 等しいですか?
注:異なるスキームは異なるサブ退避位置も考慮されています。
データ範囲:
データのための1セット:1≤n≤500,1≤m≤50、K = 1 、
グループ2のグループ3データ:1≤n≤500,1≤m≤50、K = 2
〜4グループデータに対してグループ5:1≤n≤500,1≤m≤50、K = M
グループ7データグループ1:1≤n≤500,1≤m≤50,1≤k≤m
用グループ9データ群1:1≤n≤1000,1≤m≤100,1≤k≤m
データの全10セットについて:1≤n≤1000,1≤m≤200,1≤k≤m
-------------------------------------------------- ----私は------------------------------------------行を分割しています----------
ソリューション:状態それほど明白な問題は、我々は最初の問題を解決するためのエントリポイントを見つける必要があります。
この質問は特性が観察され、我々は、Bに対し、Aは、各文字がはっきり選挙であると決定の2種類の投票ではない、ことがわかりました。
二つの文字列の状態A、状態B点を指し、二つのポインタで表すことができます。
この質問は非常に状態が発見された後、あります。
セットのステータス:
提供Fは(I、J、K、1)現在のj番目の文字にマッチしたA、Bのフロントi番目の文字を表し、k個のセグメントの合計を使用して、プログラムの数は、現在i番目の文字を選択しました。
Fは(I、J、K、0)はk個のセグメントの合計を使用して、現在のj番目の文字にマッチしたA、Bのフロントi番目の文字を示し、この実施形態は、i番目の文字の数を選択しません。
そして、状態遷移は次のようになります。
次のような状態では、我々は状態遷移方程式を推測することがあります。
(1)時あい== Bjに、F(I、J、K、0)= F(I-1、J、K、0)+ F(I-1、J、K、1)。
すなわち、i番目のプログラム番号の前にスキームA I-1の合計は数==現在に答えていません。
F(I、J、K、1)= F(I-1、J-1、K-1,0)+ F(I-1、J-1、K-1,1)+ F(I-1 、J-1、K、1);
第1 jはB(iは前に応答しない)+前にプログラムの数の合計に対するヒットの数の前に、i番目のA == A I-1での答えとして、すなわち、現在のプログラム番号プログラム番号i-1(前の回答にI)。
(2)場合あい!= Bjの時間、F(I、J、K、0)= F(I-1、J-1、K、0)+ F(I-1、J-1、K、1) 。
(1)に転送します。
F(I、J、K、1)= 0。
することはできません答えとして現在の数を考えると、そのプログラムはゼロです。
スキーム1において、すなわち0 Bマッチ文字と、我々は、F(I、0,0,0)= 1を知ることができ、境界条件を決定します。
だから我々は、コードを書くことができます。
#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef pair<int, int> pii;
typedef double db;
const int mod = 1e9+7;
const int N = 1e6 + 50;
int n, m, p;
int f[1010][101][101][2];
char a[N], b[N];
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}
while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
return x*f;
}
void work(){
rep(i, 0, n) f[i][0][0][0] = 1;
rep(i, 1, n) rep(j, 1, m) rep(k, 1, p){
if(a[i] == b[j]) {
f[i][j][k][0] = (f[i-1][j][k][0] + f[i-1][j][k][1])%mod;
f[i][j][k][1] = (f[i-1][j-1][k-1][0] + (f[i-1][j-1][k-1][1] + f[i-1][j-1][k][1]) % mod) % mod;
}
else {
f[i][j][k][1] = 0;
f[i][j][k][0] = (f[i-1][j][k][0] + f[i-1][j][k][1])%mod;
}
}
printf("%d\n", (f[n][m][p][1] + f[n][m][p][0])%mod);
}
void init(){
n = read(); m = read(); p = read();
scanf("%s%s", a+1, b+1);
}
int main(){
init();
work();
return 0;
}
但是我们发现,这份代码空间复杂度效率低下(2*n*m*k),无法通过此题,我们还需要优化。
于是乎,DP常用的空间优化:滚动数组优化就出现了。
观察DP转移方程,我们可以发现,每一个决策i只与前一个决策i-1有关,其他的空间都是多余的。
所以我们就可以用01方法表示。
AC代码如下:
#include<bits/stdc++.h>
#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef pair<int, int> pii;
typedef double db;
const int mod = 1e9 + 7;
const int N = 1e6 + 50;
int n, m, p;
int f[2][220][220][2];
char a[N], b[N];
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}
while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
return x*f;
}
void work(){
int val = 1;
f[0][0][0][0] = f[1][0][0][0] = 1;
rep(i, 1, n) {
rep(j, 1, m) rep(k, 1, p){
if(a[i] == b[j]) {
f[val][j][k][0] = (f[val^1][j][k][0] + f[val^1][j][k][1])%mod;
f[val][j][k][1] = (f[val^1][j-1][k-1][0] + (f[val^1][j-1][k-1][1] + f[val^1][j-1][k][1]) % mod) % mod;
}
else {
f[val][j][k][1] = 0;
f[val][j][k][0] = (f[val^1][j][k][0] + f[val^1][j][k][1])%mod;
}
}
val ^= 1;
}
printf("%d\n", (f[n&1][m][p][1] + f[n&1][m][p][0])%mod);
}
void init(){
n = read(); m = read(); p = read();
scanf("%s%s", a+1, b+1);
}
int main(){
init();
work();
return 0;
}
总结:这道题作为线性DP的练习题(NOIP的题),有一定的思维难度,对DP思维提升有很大的帮助。