思路:
题目的主要信息:
- 从字符串s的子序列中找到等于"niuniu"的子序列数
- 子序列即原串的删去任意个字符(包括0和全部)
- 字符串s长度默认不小于10,结果需取模1e9+7
方法一:动态规划
具体做法:
我们用表示以i-1为结尾的s子序列中出现以j-1为结尾的字符串t的个数,对于某一个字符,我们比较,如果其相等,我们选择匹配或者不匹配(不匹配就是删去这个,另外匹配的意思),二者相加即可;如果不相等,那只能选不匹配,于是转移方程就是这两个:
- 字符相等:
- 字符不相等:
class Solution { public: int solve(string s) { int mod = 1e9 + 7; int n = s.length(); vector<vector<int> > dp(7, vector<int>(n + 1, 0));//以i-1为结尾的字符串s子序列中出现以j-1为结尾的字符串t的个数为dp[i][j] for(int i = 0; i <= n; i++) dp[0][i] = 1; string t = "niuniu"; for(int i = 1; i <= 6; i++){ for(int j = 1; j <= n; j++){ if(t[i - 1] == s[j - 1]) //比较是否相等 dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1]; //匹配或者不匹配 else{ dp[i][j] = dp[i][j - 1]; //不匹配 } } } return dp[6][n]; } };
复杂度分析:
- 时间复杂度:,相当于一次遍历
- 空间复杂度:,7个一维数组
方法二:动态规划空间改进
具体做法:
方法一我们可以看到,我们只用到了和,于是我们可以考虑将6次的循环放入内部,每次都计算该为下以某个字符结尾的子序列数量,一直累加到字符串s的结尾。如图所示,图中前半部分是遍历所有字符,后半部分只显示相同的字符:
class Solution { public: int solve(string s) { int mod = 1e9 + 7; int n = s.length(); vector<int> dp(6, 0); //dp[j]表示截至到访问的字符串i字符,字符串(0,j)出现在子序列的次数 string t = "niuniu"; for(int i = 0; i < n; i++){ for(int j = 0; j < 6; j++){ if(s[i] == t[j]){ //相等 if(j == 0){ dp[j]++; dp[j] %= mod; }else{ dp[j] += dp[j - 1]; dp[j] %= mod; } } } } return dp[5]; } };
复杂度分析:
- 时间复杂度:,相当于一次遍历
- 空间复杂度:,常数个变量,无额外空间