Short Palindrome
题目大意:
给字符串 S(1<=length(S)<=1e6) ,全由小宝字母组成,求出有多少对 (a,b,c,d)(a<b<c<d) 满足 Sa==SdandSb==Sc .解法:
由于只有26种字母,并不多,我们可以枚举字母来处理,假设现在枚举 Sa==Sd=′a′andSb==Sc=′b′ 我们定义:- A数组表示字母 Sa 出现的位置,在哪个位置出现,就计数加1
- B数组表示字母 Sb 出现的位置p,并且前面的 Sa 的数量,假设 S100==′b′ , (∑99i=1Si==′a′)==50 那么数组 B100==50
现在来枚举’b’的所有位置p,则:
result+=(∑i=1p−1Bi)∗(∑j=p+11e6Sj==′a′)
这表示位置p左边的ab对,乘以右边的位置d,也就是说我们枚举的是c。
上面乘号两边,都可以利用树状数组来 O(log) 的时间内算出。
代码:
#include <bits/stdc++.h>
typedef long long LLONG;
typedef std::vector<int> VI;
const int N = 1e6 + 10;
const LLONG MOD = 1e9 + 7;
char s[N];
LLONG a[N];
LLONG b[N];
VI idxSet[30];
inline void add(LLONG& rlt, LLONG av) {
rlt += av;
if (rlt >= MOD)
rlt %= MOD;
}
inline int lowBit(int k) { return k & -k;}
inline void update(LLONG x[], int p, LLONG v) {
for (; p < N; p += lowBit(p)) {
x[p] += v;
}
}
inline LLONG query(LLONG x[], int l, int r) {
LLONG rlt = 0;
for (; r; r -= lowBit(r))
rlt += x[r];
for (--l; l > 0; l -= lowBit(l))
rlt -= x[l];
return rlt;
}
int main() {
int t;
scanf("%s", s);
for (int i = 0; s[i]; ++i) {
int idx = s[i] - 'a';
idxSet[idx].push_back(i + 1);
}
LLONG rlt = 0;
for (int i = 0; i < 26; ++i) {
for (int j = 0; j < (int)idxSet[i].size(); ++j) {
update(a, idxSet[i][j], 1LL);
}
for (int j = 0; j < 26; ++j) {
for (int k = 0; k < (int)idxSet[j].size(); ++k) {
int p = idxSet[j][k];
LLONG vv = query(a, p + 1, N - 1);
LLONG vvv = query(b, 0, p - 1);
add(rlt, vv * vvv);
LLONG v = query(a, 0, p - 1);
update(b, p, v);
}
for (int k = 0; k < (int)idxSet[j].size(); ++k) {
int p = idxSet[j][k];
LLONG v = query(a, 0, p - 1);
update(b, p, -v);
}
}
for (int j = 0; j < (int)idxSet[i].size(); ++j) {
update(a, idxSet[i][j], -1);
}
}
printf("%lld\n", rlt);
return 0;
}