1. 程式人生 > >P2679 子串

P2679 子串

scanf int 完全 scan cstring 傳送門 匹配 劃分 names

傳送門

十分顯然的DP

設 f [ i ] [ j ] [ k ] [ 1/0 ] 表示當前考慮到 A 串第 i 位,匹配到 B 串第 j 位,已經劃分了 k 分,當前為 選 or 不選

如果 A [ i ] ≠ B [ j ] 那麽當前位不能選,則只有一個轉移 f [ i ] [ j ] [ k ] [ 0 ] = f [ i-1 ] [ j ] [ k ] [ 0 ] + f [ i-1 ] [ j ] [ k ] [ 1 ]

如果 A [ i ] = B [ j ] 那麽可以考慮選擇當前位,就多了一個轉移

f [ i ] [ j ] [ k ] [ 1 ] = f [ i-1 ] [ j-1 ] [ k-1 ] [ 0 ] + f [ i-1 ] [ j-1 ] [ k ] [ 1 ] + f [ i-1 ] [ j-1 ] [ k-1 ] [ 1 ]

註意轉移時如果上一位也有選,那麽當前位可以與上一位合並也可以不合並,單獨分出一位

註意空間完全不夠,所以要滾動數組優化掉 i

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
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<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1007,M=207,mo=1e9+7; inline int fk(int x) { return x>=mo ? x-mo : x; }//這樣取余比較快 int n,m,t; char sa[N],sb[M];
int f[2][M][M][2]; int main() { n=read(); m=read(); t=read(); scanf("%s",sa+1); scanf("%s",sb+1); f[0][0][0][0]=1; for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) for(int k=0;k<=t;k++) { f[i&1][j][k][0]=fk( f[(i-1)&1][j][k][0]+f[(i-1)&1][j][k][1] ); if(j&&k&&sa[i]==sb[j])//要註意此時j,k不能為0 f[i&1][j][k][1]=fk( f[(i-1)&1][j-1][k][1]+ fk(f[(i-1)&1][j-1][k-1][0]+f[(i-1)&1][j-1][k-1][1]) ); else f[i&1][j][k][1]=0;//因為是滾動數組,所以註意如果不能選一定要記得把值賦為0 } printf("%d",fk( f[n&1][m][t][0]+f[n&1][m][t][1] )); return 0; }

P2679 子串