做法一(时间复杂度o(n),空间复杂度o(1))
首先如果一个单词中没有重复字母是一个比较简单的dp问题。比如如果找单词为“air”的子序列,我们只需要开一个dp[3]数组,dp[0]记录前面“a”的个数,dp[1]记录前面为“ai”的个数,dp[2]记录前面“air”的个数即可,更新分别以dp[0]++;dp[1]+=dp[0];dp[2]+=dp[1]进行顺序的更新即可,显然最后dp[2]即是我们想要的答案。但是我们这次要找的子序列“warrant”中有重复字母,不能像上面一样简单的更新。不过可以运用上面的思想,对于出现多次的字母,我们先更新后面的再更新前面的,这样就可以避免重复计算。代码如下:
const int maxn=2e6+5; const int mode=1e9+7; int dp[7]; class Solution { public: /** * 返回答案对1e9+7取模后的值 * @param str string字符串 * @return int整型 */ int numberofanswer(string str) { // write code here int i,n; n=str.size(); long long sum=0; for(i=0;i<n;i++) { if(str[i]=='w')dp[0]++; else if(str[i]=='a'){dp[4]+=dp[3];dp[1]+=dp[0];dp[4]%=mode;dp[1]%=mode;} else if(str[i]=='r'){dp[3]+=dp[2];dp[2]+=dp[1];dp[2]%=mode;dp[3]%=mode;} else if(str[i]=='n'){dp[5]+=dp[4];dp[5]%=mode;} else if(str[i]=='t'){dp[6]+=dp[5];dp[6]%=mode;} } return dp[6]; } };
做法二(时间复杂度o(n),空间复杂度o(n))
当然,如果想不明白更新顺序的话,也可用其他办法处理相同字母的问题,这里提供其中的一种方法。我们可以一单词的第二个r为分界点。把单词分为三部分“war”+“r”+“ant”,这样这三个部分都没有重复字母,然后我们枚举每个r,计算它当第二个r的权值,即前缀“war”的个数乘上后缀“ant”的个数,求和后即为我们要的答案。代码如下:
const int maxn=2e6+5; const long long mode=1e9+7; long long qz[maxn]; long long hz[maxn]; int dp[2]; class Solution { public: /** * 返回答案对1e9+7取模后的值 * @param str string字符串 * @return int整型 */ int numberofanswer(string str) { // write code here int i,n; n=str.size(); long long sum=0; for(i=0;i<n;i++) { if(str[i]=='w')dp[0]++; else if(str[i]=='a'){dp[1]+=dp[0];dp[1]%=mode;} else if(str[i]=='r'){sum+=dp[1];sum%=mode;} qz[i]=sum; } sum=0; dp[0]=dp[1]=0; for(i=n-1;i>=0;i--) { if(str[i]=='t')dp[0]++; else if(str[i]=='n'){dp[1]+=dp[0];dp[1]%=mode;} else if(str[i]=='a'){sum+=dp[1];sum%=mode;} hz[i]=sum; } sum=0; for(i=2;i<n-2;i++) { if(str[i]=='r') { sum+=qz[i-1]*hz[i+1]; sum%=mode; } } int sum2=sum; return sum2; } };