动态规划解
方案一:
二维数组dp
思路:
s="baad";
t="ba";
dp[i][j]
为,t
中前i
个字符串与 s
中的 前j
个字符串的子串数。
ba
ba
dp[2][2]=1
baa
ba
dp[2][3]=dp[1][2]+dp[2][2]
对于 dp[i][j]
,如果 s[j]==t[i]
,表明当前两个串的最后字符对应,则dp数为双方都除开当前字符后的dp数+s串不算当前末尾字符串的dp数。
对于 dp[i][j]
,如果 s[j]!=t[i]
,则 dp 数只为 上一个(j-1) dp数(dp[i][j-1]
)。
d p [ i ] [ j ] = { d p [ i − 1 ] [ j − 1 ] + d p [ i ] [ j − 1 ] s[j]==t[i] d p [ i ] [ j − 1 ] s[j]!=t[i] dp[i][j]= \begin{cases} dp[i-1][j-1]+dp[i][j-1]& \text{s[j]==t[i]}\\dp[i][j-1]& \text{s[j]!=t[i]} \end{cases} dp[i][j]={dp[i−1][j−1]+dp[i][j−1]dp[i][j−1]s[j]==t[i]s[j]!=t[i]
int dp[1002][1002];
int numDistinct(char * s, char * t){
memset(dp,0,sizeof(dp));
int MAX=0x80000000-1; // 根据题目说明,结果一定为32位数内
int sum=0;
int lt=strlen(t),ls=strlen(s);
if(lt>ls)return 0;
for(int i=0;i<=ls;i++) dp[0][i]=1;
for(int i=1;i<=lt;i++){
for(int j=i;j<=ls;j++){
dp[i][j]=dp[i][j-1];
if(s[j-1]==t[i-1] && dp[i-1][j-1]<=MAX-dp[i][j-1]){ // 丢弃超过int范围内的数据
dp[i][j]+=dp[i-1][j-1];//dp[i-1][j-1];
}
}
}
return dp[lt][ls];
}
一维数组
在方法一中,因为每轮递推中 dp[i][j]都是继承于 dp[i−1][j] 和 dp[i−1][j−1] 的,因此我们可以取消 i 这个维度,直接用一维数组滚动递推。这样一来其实问题也就可以看做一个 0−1 背包问题。计算在 s 的子序列中 t 出现的个数,其实就相当于在 s 中按顺序找出 x 字符,其中 x 是 t 的长度,使之能构成 t。问题就转化为用 s 提供的石头填满容量为 x 的背包,每个石头需要按顺序选择且只能选择一次,因此是一个 0−1 背包问题。
int min(int a,int b){
if(a<b) return a;
return b;
}
int dp[1002];
int numDistinct(char *s,char *t){
memset(dp,0,sizeof(dp));
int MAX=0x80000000-1;
int lt=strlen(t),ls=strlen(s);
dp[0]=1;
for(int i=1;i<=ls;i++){
for(int j=min(lt,i);j>=1;j--){
if(s[i-1]==t[j-1] && dp[j]<=MAX-dp[j-1]){
dp[j]=dp[j]+dp[j-1];
}
//printf("dp[%d]=%d\n",j,dp[j]);
}
}
return dp[lt];
}