Senior's String
學姐姐非常喜歡字符串,所以學弟送給了她兩個字符串作爲禮物。 兩個字符串分別爲X,Y。她非常開心,但在開心之餘她還想考考學弟。 她定義L爲X與Y的最長公共子序列的長度(子序列在字符串內不一定連續,一個長度爲L的字符串有2L個子序列,包括空子序列)。 現在學姐姐取出了X的所有長度爲L的子序列,並要求學弟回答在這些子序列中,有多少個是Y的子序列。 因爲答案可能很大,所以學弟只需要回答最終答案模109+7。
第一行包含一個整數T,表示測試數據組數。 對於每組測試數據: 第一行包含一個非空字符串X。 第二行包含一個非空字符串Y。 字符串由小寫英文字母構成。 1≤|X|,|Y|≤1000, |X|表示X的長度。
對於每組測試數據輸出一個整數,表示對應的答案。
2 a b aa ab
1 2
題解:
首先我們用O(n2)的動態規劃算法處理出dp數組,dp[i][j]表示X串的前i個字符和Y串的前j個字符的最長公共子序列的長度,在這個基礎上我們再進行一個動態規劃。用f[i][j]表示在X串的前i個字符中,有多少個長度爲dp[i][j]的子序列在Y的前j個字符中也出現了。轉移:若dp[i−1][j]==dp[i][j],則f[i][j]+=f[i−1][j],表示i這個字符不選;再考慮選i這個字符,找到Y串前j個字符中最靠後的與X[i]匹配的字符的位置,設爲p,若dp[i−1][p−1]+1==dp[i][j],則f[i][j]+=f[i−1][p−1]。最終的答案即爲f[n][m]。複雜度O(n2)。
看着題解給的思路,想不明白。
然後又用到了兩重動態規劃,第一個很好理解,第二個其實就是對於每一個字符串x的字符,看它在不在長度爲L“相等”的子序列裏面,不在的話是第一種,在的話(即與x字符串之前的字符重複)是第二種。
代碼:
#include <iostream>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int mod=1000000007;
int dp[1003][1003];
int f[1007][1007];
int meet[1007][27];
string x,y;
int main()
{
int Test,m,n,i,j;
cin>>Test;
while(Test--)
{
cin>>x>>y;
m=x.length();
n=y.length();
memset(dp,0,sizeof(dp));
for(i=0;i<m;i++)
{
for(j=0;j<n;j++)
{
if(x[i] == y[j])
{
dp[i+1][j+1] = dp[i][j]+1;
}
else
{
dp[i+1][j+1] = max(dp[i][j+1],dp[i+1][j]);
}
}
}
memset(f,0,sizeof(f));
memset(meet,0,sizeof(meet));
for(i=0;i<n;i++)
{
for(j=0;j<26;j++)
{
meet[i+1][j] = meet[i][j];
}
meet[i+1][y[i]-'a']=i+1;
}
for(i=0;i<=m;i++)
{
for(j=0;j<=n;j++)
{
if(dp[i][j]==0)
{
f[i][j]=1;
continue;
}
if(dp[i-1][j]==dp[i][j])
{
(f[i][j]=f[i-1][j])%=mod;
}
int p= meet[j][x[i-1]-'a'];
if(dp[i-1][p-1]+1==dp[i][j])(f[i][j]+=f[i-1][p-1])%=mod;
}
}
cout<<f[m][n]%mod<<endl;
}
return 0;
}