leetcode3333 找到初始输入字符串II
用键盘输入字符时,可能因为在一个键上停留太久,导致同一个字符被输入多次。给定word表示最终显示的字符串,以及整数k,表示希望输入字符串的最少长度,求希望输入串的总方案数,对1E9+7取模。
1<=|word|<=5E5; 1<=k<=2000; word只包含小写字母
分析:
1、假设最终串的长度为n,对其分组循环,把相同的字符串合并,假设最终分成了m组。对于每个分组,可以选1个、2个、3个。。。
2、由于k较小,正面求的计算量很大,考虑逆向,计算小于k的方案数,再用所有方案数将其减去即为答案。
3、如果不考虑长度限制,根据乘法原理,所有方案数为各分组数量的乘积。
4、小于k的方案数分别枚举求解,设dp[i][j]表示前i个分组选出j个字符的方案数,枚举选1个、2个、3个的情况进行转移,再求和,时间复杂度为O(mk^2)。
5、将转移的求和部分用前缀和优化,可以将时间复杂度优化到O(mk)。
6、由于每次计算只用到了上一次的结果,可以用滚动数组将空间复杂度优化到O(k)。
int A[500005]; mint dp1[2005], dp2[2005], pre[2005]; class Solution { public: int possibleStringCount(string word, int k) { int n = word.size(); if (n < k) { return 0; } int m = 0; mint ans = 1; for (int i = 0, j; i < n; i++, i = j) { for (j = i + 1; j < n && word[j] == word[i]; j++); m += 1; A[m] = j - i; ans *= A[m]; } if (m >= k) { return ans.val(); } auto getpre = [&]() { pre[0] = dp1[0]; for (int i = 1; i < k; i++) { pre[i] = dp1[i] + pre[i-1]; } }; auto sum = [&](int l, int r) -> mint { if (l <= 0) return pre[r]; return pre[r] - pre[l-1]; }; dp1[0] = 1; for (int j = 1; j < k; j++) { dp1[j] = 0; } getpre(); for (int i = 1; i <= m; i++) { dp2[0] = 0; for (int j = 1; j < k; j++) { int z = std::min(A[i], j); dp2[j] = sum(j-z, j-1); } std::swap(dp1, dp2); getpre(); } for (int i = 0; i < k; i++) { ans -= dp1[i]; } return ans.val(); } };
标签:
背包dp
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理