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();
    }
};
                    
                
                
            
        
浙公网安备 33010602011771号