一开始第一反映是用暴搜+回溯剪枝,妥妥的超时,见numDistinct0函数。
后来想到这跟公共子串有点类似,满足最优子结构和重叠问题,因此可用DP。
状态转移方程如下:
{ dp[i-1,j-1]+dp[i-1][j] , 当s[i]==s[j],0<i<s.size(),0<j<t.size(),
dp[i,j]={ dp[i-1][j], 当s[i]!=s[j] ,0<i<s.size(),0<j<t.size(),
{ 0, i<j(保证t中前j个都匹配到), s.size()-i>t.size()-j(保证s后续元素数够匹配)
初始条件:dp[i][1]=1 if s[i]==t[0]
复杂度O(nm)
class Solution {
public:
int cnt,n,m;
int *num;
void count(string &s,int ids,string &t,int idt){
if(idt>=m){
cnt++;
return;
}
if(m-ids<n-idt||num[t[idt]]<=0)return;
if(s[ids]==t[idt]){
num[s[ids]]--;
count(s,ids+1,t,idt+1);
num[s[ids]]++;
}
num[s[ids]]--;
count(s,ids+1,t,idt);
num[s[ids]]++;
}
int numDistinct0(string s, string t) {
n=s.size();
m=t.size();
cnt=0;
num=new int[256];
memset(num,0,sizeof(int)*256);
for(int i=0;i<n;++i)num[s[i]]++;
count(s,0,t,0);
return cnt;
}
int numDistinct (string s, string t) {
n=s.size();
m=t.size();
int i,j,**d=new int*[n+1];
for(i=0;i<=n;++i){
d[i]=new int[m+1];
memset(d[i],0,sizeof(int)*(m+1));
}
for(i=0;i<n;++i){
if(s[i]==t[0])d[i+1][1]=1;
}
for(i=1;i<=n;++i){
for(j=1;j<=i&&j<=m;++j){
if(n-i<m-j)continue;
d[i][j]+=d[i-1][j];
if(s[i-1]==t[j-1])
d[i][j]+=d[i-1][j-1];
}
}
return d[n][m];
}
};