題解/算法 {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;
}