2949. 统计美丽子字符串 II
LeetCode 2947. 统计美丽子字符串 I(前缀和、枚举)的提升版本
思路:我们可以预处理出字符串s中元音字母和辅音字母差值的情况,也就是前缀和。美丽子字符串[i,j]一定满足v[j] == v[i-1]。
再观察第二个条件,会发现美丽子字符串的长度L = j-i+1,需要满足(L/2)^2%k = = 0也就是 L^2%4k = = 0。
**对于4k,L^ 2 是可以被4k整除的,那么我们对4k进行质数分解。对于每一个质数a的幂次b,L都必须被 a^(b/2)整除,注意这里的b/2是向上取整。这样就产生了一个新的kk,条件二就变为L%kk==0,也就是(j-i+1)%kk=0
这样在遍历右端点时,我们在满足条件一的同时(v[j] == v[i-1]),还需要满足条件二((j-i+1)%kk=0)**
class Solution {
public:
long long beautifulSubstrings(string s, int k) {
int n=s.size();
vector<int> v(n,0);
//预处理出字符串s中元音字母和辅音字母差值的情况
for(int i=0;i<n;i++){
if(i!=0) v[i]=v[i-1];
if(s[i]=='a'||s[i]=='e'||s[i]=='i'||s[i]=='o'||s[i]=='u'){
v[i]--;
}else v[i]++;
}
//找出L需要被kk整除
int kk=1;
k=k*4;
for(int i=2;i<=k;i++){
int t=i*i;
while(k%t==0){
kk*=i;
k/=t;
}
if(k%i==0){
kk*=i;
k/=i;
}
}
if(k){
kk*=k;
}
//保存前面遍历过的情况:下标%kk、v[i]、出现的次数
map<pair<int,int>,int> mp;
//这里是为了让[0,i]本身就满足的情况+1
//kk-1是因为(-1)%kk
mp.insert({{(kk-1)%kk,0},1});
long long ans=0;
for(int i=0;i<n;i++){
if(mp.count({i%kk,v[i]})) ans+=mp[{i%kk,v[i]}];
mp[{i%kk,v[i]}]++;
}
return ans;
}
};