題解/算法 {2949. 统计美丽子字符串 II}

本文介绍了如何解决LeetCode上的统计美丽子字符串II问题,通过计算字符串中元音和辅音的差值,以及利用条件2中的模数性质,优化代码以减少时间复杂度。关键在于质因数分解和使用Map数据结构来加速循环。
摘要由CSDN通过智能技术生成

題解/算法 {2949. 统计美丽子字符串 II}

@LINK: https://leetcode.cn/problems/count-beautiful-substrings-ii/;

Sum[i]S[0...i]裡的(元音個數 減去 輔音個數), 那麼對於條件1(一個字符串的元音輔音個數相同) 就對應於 如果S[l...r]是符合條件1的 那麼就有Sum[r] == Sum[l-1];
. 處理子字符串問題, 經常會用到這個技巧, 即把[l...r] 變成是[0...r] 減去 [0...(l-1)];

關鍵是對於條件2, 比如對於S[l...r] 假設他已經滿足條件1了 (理解這點很重要 因為我們上面這步 已經保證了這點), 他的長度是L = r-l+1, 他要滿足(L/2)^2 = 0 (%K), 此時需要你對數學取模運算的理解了, 化簡 L^2 = 0 (% 4K), 此時的L 他滿足哪些特性呢? 即L的平方 是 4K的倍數, 令4K的質因數分解是 p^k * ..., 令kk = k/2(上取整), 那麼L的質因數 一定是p^(>= kk) * ...; 我們令M = p^kk * ... 則所有滿足條件的L 一定是M的倍數 (因為(L= k*M)^2一定是M^2的倍數 而M^2一定是4K的倍數);

因此 得到以下代碼:

int64_t ANS = 0;
FOR_( i, 0, N-1){
    if( multiset<char>{'a','e','i','o','u'}.count( S[i]) > 0){ ++ sum;}
    else{ --sum;}
    Sum[i] = sum;

    //> `[0...i]`
    if( ((i+1)%M==0) && Sum[i]==0){ ++ ANS;}
    FOR_( l, 0, i-1){ // `[l+1, ..., i]`
        if( (i-l)%M==0 && Sum[l]==Sum[i]){ ANS ++;}
    }
}

但這會超時 因為FOR( l)這個循環, 要優化他; 對於(i-l)%M==0 他等價於i==l (%M), 使用map< pair(Sum[i], i%M), int> Cont來存儲每一個i: 0123...(i-1)Sum[i], i%M的值; 那麼這個循環 就變成了ANS += Cont[ {Sum[i], i^M}];

long long beautifulSubstrings(string S, int K) {
    int N = S.size();
    int M = 1;
    { // 一個數的质因数分解 ($O(\sqrt{a})$)
        auto ___a = 4*K;
        map< decltype(___a), int> ___Factors; // `a`的質因數分解
        for( int ___p = 2; ___p <= ___a / ___p; ++___p){
            if( ___a % ___p == 0){
                int ___k = 0;
                while( ___a % ___p == 0){
                    ++ ___k;
                    ___a /= ___p;
                }
                ___Factors[ ___p] = ___k;
            }
        }
        if( ___a > 1){
            ___Factors[ ___a] = 1;
        }
        for( auto [a,b] : ___Factors){
            M *= Integer::Power( a, (b + 1) / 2);
        }
    } // 一個數的质因数分解 ($O(\sqrt{a})$)
    map< pair<int,int>, int> Cont; // {Sum[i], i%M}
    int sum = 0;
    int64_t ANS = 0;
    FOR_( i, 0, N-1){
        if( multiset<char>{'a','e','i','o','u'}.count( S[i]) > 0){ ++ sum;}
        else{ --sum;}

        //> `[0...i]`
        if( ((i+1)%M==0) && sum==0){ ++ ANS;}
        //> `[l...i]`
        ANS += Cont[ {sum, i%M}];

        Cont[ {sum, i%M}] ++;
    }
    return ANS;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值