chenfy27的刷题记录

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

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();
}
};

posted on   chenfy27  阅读(4)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示